| /********************************************************************* |
| * Copyright (c) 2011 SpringSource, a division of VMware, Inc. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| **********************************************************************/ |
| |
| package org.eclipse.virgo.ide.jdt.internal.core.classpath; |
| |
| import java.util.Queue; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentLinkedQueue; |
| import java.util.concurrent.LinkedBlockingQueue; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import org.eclipse.core.resources.WorkspaceJob; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.IJobChangeEvent; |
| import org.eclipse.core.runtime.jobs.IJobChangeListener; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.jobs.JobChangeAdapter; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| import org.eclipse.virgo.ide.jdt.core.JdtCorePlugin; |
| import org.eclipse.virgo.ide.jdt.internal.core.util.ClasspathUtils; |
| import org.eclipse.virgo.ide.manifest.core.IBundleManifestChangeListener.Type; |
| |
| /** |
| * {@link WorkspaceJob} that triggers the class path container refresh. |
| * |
| * @author Christian Dupuis |
| * @since 1.0.0 |
| */ |
| public class ServerClasspathContainerUpdateJob extends WorkspaceJob { |
| |
| /** Internal cache of scheduled and <b>unfinished</b> update jobs */ |
| private static final Queue<IJavaProject> SCHEDULED_PROJECTS = new ConcurrentLinkedQueue<IJavaProject>(); |
| |
| /** |
| * Manages the jobs, so there will not be a huge amount of concurrent updates |
| */ |
| private static LimitConcurrentClasspathUpdatesListener limitConcurrentClasspathUpdatesListener = new LimitConcurrentClasspathUpdatesListener(); |
| |
| /** |
| * The {@link IJavaProject} this jobs should refresh the class path container for |
| */ |
| private final IJavaProject javaProject; |
| |
| private final Set<Type> types; |
| |
| /** |
| * Private constructor to create an instance |
| * |
| * @param javaProject the {@link IJavaProject} the class path container should be updated for |
| * @param types the change types happened to the manifest |
| */ |
| private ServerClasspathContainerUpdateJob(IJavaProject javaProject, Set<Type> types) { |
| super("Updating bundle classpath container for project '" + javaProject.getElementName() + "'"); |
| this.javaProject = javaProject; |
| this.types = types; |
| } |
| |
| /** |
| * Returns the internal {@link IJavaProject} |
| */ |
| public IJavaProject getJavaProject() { |
| return this.javaProject; |
| } |
| |
| /** |
| * Runs the job in the context of the workspace. Simply delegates refreshing of the class path container to |
| * {@link ClasspathUtils#updateClasspathContainer(IJavaProject, IProgressMonitor)} . |
| */ |
| @Override |
| public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { |
| if (!this.javaProject.getProject().isOpen() || monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| try { |
| ClasspathUtils.updateClasspathContainer(this.javaProject, this.types, monitor); |
| } catch (Exception e) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| return new Status(IStatus.OK, JdtCorePlugin.PLUGIN_ID, "Updated SpringSource dm Server classpath container"); |
| } |
| |
| /** |
| * Helper method to schedule a new {@link ServerClasspathContainerUpdateJob} . |
| * |
| * @param javaProject the {@link IJavaProject} the class path container should be updated for |
| * @param types the change types of the manifest |
| */ |
| public static void scheduleClasspathContainerUpdateJob(IJavaProject javaProject, Set<Type> types) { |
| if (javaProject != null && !SCHEDULED_PROJECTS.contains(javaProject) && types.size() > 0 |
| && ClasspathUtils.hasClasspathContainer(javaProject)) { |
| newClasspathContainerUpdateJob(javaProject, types); |
| } |
| } |
| |
| /** |
| * Creates a new instance of {@link ServerClasspathContainerUpdateJob} and configures required properties and |
| * schedules it to the workbench. |
| */ |
| private static ServerClasspathContainerUpdateJob newClasspathContainerUpdateJob(IJavaProject javaProject, Set<Type> types) { |
| ServerClasspathContainerUpdateJob job = new ServerClasspathContainerUpdateJob(javaProject, types); |
| // job.setRule(ResourcesPlugin.getWorkspace().getRuleFactory().buildRule()); |
| job.setPriority(Job.BUILD); |
| job.setSystem(true); |
| job.addJobChangeListener(new DuplicateJobListener()); |
| job.addJobChangeListener(limitConcurrentClasspathUpdatesListener); |
| job.sleep(); |
| limitConcurrentClasspathUpdatesListener.schedule(job); |
| |
| return job; |
| } |
| |
| /** |
| * Internal {@link IJobChangeListener} to detect duplicates in the scheduled list of |
| * {@link ServerClasspathContainerUpdateJob Jobs}. |
| */ |
| private static class DuplicateJobListener extends JobChangeAdapter implements IJobChangeListener { |
| |
| @Override |
| public void done(IJobChangeEvent event) { |
| SCHEDULED_PROJECTS.remove(((ServerClasspathContainerUpdateJob) event.getJob()).getJavaProject()); |
| } |
| |
| @Override |
| public void scheduled(IJobChangeEvent event) { |
| SCHEDULED_PROJECTS.add(((ServerClasspathContainerUpdateJob) event.getJob()).getJavaProject()); |
| } |
| } |
| |
| /** |
| * Internal {@link IJobChangeListener} to limit the number of concurrent builds. |
| */ |
| private static class LimitConcurrentClasspathUpdatesListener extends JobChangeAdapter implements IJobChangeListener { |
| |
| /** |
| * Queue with the jobs which are scheduled, used to make sure a limited amount of projects is being rebuilt at |
| * the same time |
| */ |
| private final Queue<ServerClasspathContainerUpdateJob> SCHEDULED_JOBS = new LinkedBlockingQueue<ServerClasspathContainerUpdateJob>(); |
| |
| /** |
| * Maximum number of concurrent jobs, defaults to the number of available processors |
| */ |
| private final int maxNrOfConcurrentJobs = Runtime.getRuntime().availableProcessors(); |
| |
| /** Holds the number of builds in progress */ |
| private final AtomicInteger nrOfBuildingProjects = new AtomicInteger(0); |
| |
| @Override |
| public void done(IJobChangeEvent event) { |
| this.nrOfBuildingProjects.decrementAndGet(); |
| startNextJob(); |
| } |
| |
| @Override |
| public void running(IJobChangeEvent event) { |
| ServerClasspathContainerUpdateJob job = (ServerClasspathContainerUpdateJob) event.getJob(); |
| StatusManager.getManager().handle(new Status(IStatus.INFO, JdtCorePlugin.PLUGIN_ID, |
| "Updating classpath of: " + job.javaProject.getProject().getName() + ". Queue size: " + this.SCHEDULED_JOBS.size())); |
| } |
| |
| @Override |
| public void scheduled(IJobChangeEvent event) { |
| this.nrOfBuildingProjects.incrementAndGet(); |
| } |
| |
| private void schedule(ServerClasspathContainerUpdateJob job) { |
| this.SCHEDULED_JOBS.offer(job); |
| startNextJob(); |
| } |
| |
| private void startNextJob() { |
| if (this.nrOfBuildingProjects.get() < this.maxNrOfConcurrentJobs) { |
| Job job = this.SCHEDULED_JOBS.poll(); |
| if (job != null) { // null is returned when queue is empty |
| job.schedule(); |
| } |
| } |
| } |
| } |
| } |