/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ivyde.internal.eclipse.resolve;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.sort.ModuleDescriptorSorter;
import org.apache.ivy.core.sort.WarningNonMatchingVersionReporter;
import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
import org.apache.ivy.plugins.circular.WarnCircularDependencyStrategy;
import org.apache.ivy.plugins.version.VersionMatcher;
import org.apache.ivyde.eclipse.IvyDEException;
import org.apache.ivyde.internal.eclipse.CachedIvy;
import org.apache.ivyde.internal.eclipse.IvyDEMessage;
import org.apache.ivyde.internal.eclipse.IvyMarkerManager;
import org.apache.ivyde.internal.eclipse.IvyPlugin;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
/**
* Eclipse classpath container that will contain the ivy resolved entries.
*/
public class IvyResolveJob extends Job {
private static final int MONITOR_LENGTH = 1000;
private static final int IVY_LOAD_LENGTH = 100;
private static final int POST_RESOLVE_LENGTH = 100;
private static final int WAIT_BEFORE_LAUNCH = 1000;
private final List resolveQueue = new ArrayList();
public IvyResolveJob() {
super("IvyDE resolve");
setUser(false);
// computing the classpath is somehow building
setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule());
}
public IStatus launchRequest(ResolveRequest request, IProgressMonitor monitor) {
synchronized (resolveQueue) {
resolveQueue.add(request);
}
return run(monitor);
}
public void addRequest(ResolveRequest request) {
synchronized (resolveQueue) {
resolveQueue.add(request);
}
schedule(WAIT_BEFORE_LAUNCH);
}
protected IStatus run(IProgressMonitor monitor) {
try {
return doRun(monitor);
} catch (RuntimeException e) {
IvyDEMessage.error("Resolve job failed with an unexpected exception", e);
throw e;
}
}
private IStatus doRun(IProgressMonitor monitor) {
IvyDEMessage.info("Resolve job starting...");
List toResolve;
synchronized (resolveQueue) {
toResolve = new ArrayList(resolveQueue);
resolveQueue.clear();
}
if (toResolve.isEmpty()) {
IvyDEMessage.info("Nothing to resolve");
return Status.OK_STATUS;
}
IvyDEMessage.verbose(toResolve.size() + " container(s) to resolve");
monitor.beginTask("Loading ivy descriptors", MONITOR_LENGTH);
Map/* <ModuleDescriptor, List<ResolveRequest>> */inworkspaceModules = new LinkedHashMap();
List/* <ResolveRequest> */otherModules = new ArrayList();
Map/* <ResolveRequest, Ivy> */ivys = new HashMap();
Map/* <ResolveRequest, ModuleDescriptor> */mds = new HashMap();
final MultiStatus errorsStatus = new MultiStatus(IvyPlugin.ID, IStatus.ERROR,
"Some projects fail to be resolved", null);
int step = IVY_LOAD_LENGTH / toResolve.size();
boolean forceFailOnError = false;
// Ivy use the SaxParserFactory, and we want it to instanciate the xerces parser which is in
// the dependencies of IvyDE, so accessible via the current classloader
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(IvyResolveJob.class.getClassLoader());
try {
Iterator itRequests = toResolve.iterator();
while (itRequests.hasNext()) {
ResolveRequest request = (ResolveRequest) itRequests.next();
IvyDEMessage.info("Processing resolve request " + request.toString());
forceFailOnError = forceFailOnError || request.isForceFailOnError();
monitor.subTask("loading " + request.getResolver().toString());
IProject project = request.getResolver().getProject();
if (project != null && !project.isAccessible()) {
IvyDEMessage.warn("Skipping resolve on closed project " + project.getName());
monitor.worked(step);
continue;
}
IvyDEMessage.verbose("Loading ivysettings for " + request.toString());
CachedIvy cachedIvy = request.getCachedIvy();
Ivy ivy;
try {
ivy = cachedIvy.getIvy();
} catch (IvyDEException e) {
cachedIvy.setErrorMarker(e);
IvyDEMessage.error("Failed to configure Ivy for " + request + ": "
+ e.getMessage());
errorsStatus.add(e.asStatus(IStatus.ERROR, "Failed to configure Ivy for "
+ request));
monitor.worked(step);
continue;
}
cachedIvy.setErrorMarker(null);
ivys.put(request, ivy);
// IVYDE-168 : Ivy needs the IvyContext in the threadlocal in order to found the
// default branch
ivy.pushContext();
ModuleDescriptor md;
try {
md = cachedIvy.getModuleDescriptor(ivy);
} catch (IvyDEException e) {
cachedIvy.setErrorMarker(e);
IvyDEMessage.error("Failed to load the descriptor for " + request + ": "
+ e.getMessage());
errorsStatus.add(e.asStatus(IStatus.ERROR, "Failed to load the descriptor for "
+ request));
monitor.worked(step);
continue;
} finally {
ivy.popContext();
}
cachedIvy.setErrorMarker(null);
mds.put(request, md);
if (request.isInWorkspace()) {
List requests = (List) inworkspaceModules.get(md);
if (requests == null) {
requests = new ArrayList();
inworkspaceModules.put(md, requests);
}
requests.add(request);
} else {
otherModules.add(request);
}
monitor.worked(step);
}
} finally {
Thread.currentThread().setContextClassLoader(old);
}
step = (MONITOR_LENGTH - IVY_LOAD_LENGTH - POST_RESOLVE_LENGTH) / toResolve.size();
if (inworkspaceModules.isEmpty()) {
IvyDEMessage.verbose("No module to resolve in workspace");
} else {
IvyDEMessage.info(inworkspaceModules.size() + " module(s) to resolve in workspace");
// for the modules which are using the workspace resolver, make sure
// we resolve them in the correct order
// The version matcher used will be the one configured for the first project
ResolveRequest request = (ResolveRequest) ((List) inworkspaceModules.values()
.iterator().next()).get(0);
VersionMatcher versionMatcher = ((Ivy) ivys.get(request)).getSettings()
.getVersionMatcher();
WarningNonMatchingVersionReporter vReporter = new WarningNonMatchingVersionReporter();
CircularDependencyStrategy circularDependencyStrategy = WarnCircularDependencyStrategy
.getInstance();
ModuleDescriptorSorter sorter = new ModuleDescriptorSorter(inworkspaceModules.keySet(),
versionMatcher, vReporter, circularDependencyStrategy);
List sortedModuleDescriptors = sorter.sortModuleDescriptors();
Iterator it = sortedModuleDescriptors.iterator();
while (it.hasNext()) {
ModuleDescriptor module = (ModuleDescriptor) it.next();
List requests = (List) inworkspaceModules.get(module);
IvyDEMessage.info(requests.size() + " container(s) of module " + module
+ " to resolve in workspace");
for (int i = 0; i < requests.size(); i++) {
request = (ResolveRequest) requests.get(i);
Ivy ivy = (Ivy) ivys.get(request);
ModuleDescriptor md = (ModuleDescriptor) mds.get(request);
boolean canceled = launchResolveThread(request, monitor, step, errorsStatus,
ivy, md);
if (canceled) {
IvyDEMessage.warn("Resolve job canceled");
return Status.CANCEL_STATUS;
}
}
}
}
if (otherModules.isEmpty()) {
IvyDEMessage.verbose("No module to resolve outside the workspace");
} else {
IvyDEMessage.info(otherModules.size() + " module(s) to resolve outside the workspace");
Iterator it = otherModules.iterator();
while (it.hasNext()) {
ResolveRequest request = (ResolveRequest) it.next();
Ivy ivy = (Ivy) ivys.get(request);
ModuleDescriptor md = (ModuleDescriptor) mds.get(request);
boolean canceled = launchResolveThread(request, monitor, step, errorsStatus, ivy,
md);
if (canceled) {
IvyDEMessage.warn("Resolve job canceled");
return Status.CANCEL_STATUS;
}
}
}
step = POST_RESOLVE_LENGTH / toResolve.size();
monitor.setTaskName("Post resolve");
// launch every post batch resolve
Iterator itRequests = toResolve.iterator();
while (itRequests.hasNext()) {
ResolveRequest request = (ResolveRequest) itRequests.next();
if (!request.isResolveFailed()) {
monitor.setTaskName(request.getResolver().toString());
request.getResolver().postBatchResolve();
}
monitor.worked(step);
}
if (errorsStatus.getChildren().length != 0) {
// some errors happend, stop here
if (forceFailOnError || IvyPlugin.getPreferenceStoreHelper().isErrorPopup()) {
return errorsStatus;
}
return Status.OK_STATUS;
}
return Status.OK_STATUS;
}
private boolean launchResolveThread(ResolveRequest request, final IProgressMonitor monitor,
final int step, MultiStatus errorsStatus, final Ivy ivy, final ModuleDescriptor md) {
final IStatus[] status = new IStatus[1];
final IvyResolver resolver = request.getResolver();
Runnable resolveRunner = new Runnable() {
public void run() {
status[0] = resolver.resolve(ivy, md, monitor, step);
}
};
IvyRunner ivyRunner = new IvyRunner();
if (ivyRunner.launchIvyThread(resolveRunner, ivy, monitor)) {
return true;
}
IvyMarkerManager ivyMarkerManager = IvyPlugin.getDefault().getIvyMarkerManager();
ivyMarkerManager.setResolveStatus(status[0], resolver.getProject(),
resolver.getIvyXmlPath());
switch (status[0].getCode()) {
case IStatus.CANCEL:
return true;
case IStatus.OK:
case IStatus.INFO:
IvyDEMessage.info("Successful resolve of " + request);
break;
case IStatus.ERROR:
IvyDEMessage.warn("Error on resolve of " + request + ": " + status[0].getMessage());
request.setResolveFailed(true);
errorsStatus.add(status[0]);
break;
default:
IvyPlugin.logWarn("Unknown IStatus: " + status[0].getCode());
}
return false;
}
}