/******************************************************************************* * Copyright (c) 2004, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ /* */ package org.eclipse.jem.internal.beaninfo.adapters; import java.util.logging.Level; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.*; import org.eclipse.jem.internal.beaninfo.core.BeaninfoPlugin; import org.eclipse.jem.util.logger.proxy.Logger; /** * This class is used by BeaninfoNature to handle the creation of the registry, * This class will be a singleton. It is needed to handle if UI active without * requiring UI plugin. (So headless will work too). The subclass <code>UICreateRegistryJobHandler</code> * will be instantiated in case of UI active. * @since 1.0.0 */ class CreateRegistryJobHandler { private static CreateRegistryJobHandler jobHandler = null; public static void createRegistry(BeaninfoNature nature) { synchronized (CreateRegistryJobHandler.class) { if (jobHandler == null) { if (Platform.getBundle("org.eclipse.ui") != null) { //$NON-NLS-1$ try { // There is a UI, it may not be active, but bring in UICreateRegistryJobHandler to do the // actual work since it can reference the UI. jobHandler = (CreateRegistryJobHandler) Class.forName("org.eclipse.jem.internal.beaninfo.adapters.UICreateRegistryJobHandler").newInstance(); //$NON-NLS-1$ } catch (InstantiationException e) { jobHandler = new CreateRegistryJobHandler(); } catch (IllegalAccessException e) { jobHandler = new CreateRegistryJobHandler(); } catch (ClassNotFoundException e) { jobHandler = new CreateRegistryJobHandler(); } } } } // See if Autobuild sleeping or waiting. This could be a race condition for us. We can't wait for it // because we may already have the build rule locked by our thread. No way of testing this if beginRule was used. // We can test if we are a build job (not an inline build), and if so, just go on. // Maybe we can figure out in future if we find race condition happens significant amount of time. IJobManager jobManager = Job.getJobManager(); Job currentJob = jobManager.currentJob(); if (currentJob == null || (!currentJob.belongsTo(ResourcesPlugin.FAMILY_AUTO_BUILD) && !currentJob.belongsTo(ResourcesPlugin.FAMILY_MANUAL_BUILD))) { // See if autojob is waiting or sleeping. // Give it up to a second at .2 second intervals to try (i.e. 5 tries) int tries = 5; while (isAutoWaiting() && --tries>0) { try { Thread.sleep(200); // Wait just .2 seconds to give build a chance to start. If it is still not started, then just go on. } catch (InterruptedException e) { } } if (tries==0) { Logger logger = BeaninfoPlugin.getPlugin().getLogger(); if (logger.isLoggingLevel(Level.WARNING)) logger.log("Build job waiting when trying to start beaninfo registry. Possible race.", Level.WARNING); // $NON-NLS-1$ //$NON-NLS-1$ } } jobHandler.processCreateRegistry(nature); } private static boolean isAutoWaiting() { Job[] autojobs = Job.getJobManager().find(ResourcesPlugin.FAMILY_AUTO_BUILD); for (int i = 0; i < autojobs.length; i++) { int state = autojobs[i].getState(); if (state == Job.WAITING || state == Job.SLEEPING) return true; } return false; } /** * Process the create of the registry. This should be overridden to * do what the UI needs. The UI implimentation should call doCreateRegistry at the * appropriate time. * * @param nature * * @since 1.0.0 */ protected void processCreateRegistry(final BeaninfoNature nature) { IJobManager jobManager = Job.getJobManager(); ISchedulingRule buildRule = ResourcesPlugin.getWorkspace().getRuleFactory().buildRule(); boolean gotRuleLocally = true; try { try { jobManager.beginRule(buildRule, new NullProgressMonitor()); } catch (IllegalArgumentException e) { gotRuleLocally = false; // This thread already had a rule, and it conflicted with the build rule, so we need to spawn off. } if (gotRuleLocally) doCreateRegistry(nature, new NullProgressMonitor()); } finally { jobManager.endRule(buildRule); // Whether we got the rule or not, we must do endrule. } if (!gotRuleLocally) { // Spawn off to a job and wait for it. Hopefully we don't have a deadlock somewhere. Job doCreateJob = new Job(BeanInfoAdapterMessages.UICreateRegistryJobHandler_StartBeaninfoRegistry) { protected IStatus run(IProgressMonitor monitor) { doCreateRegistry(nature, monitor); return Status.OK_STATUS; } }; doCreateJob.schedule(); while (true) { try { doCreateJob.join(); break; } catch (InterruptedException e) { } } } } /* * Do the creation. It is expected that the build rule has already been given to this thread. * It is important that the build rule be given to this thread. This is so that a build won't * start trying to create the same registry (which has happened in the past) at the same time * a different thread was trying to start the registry. You would either have a deadlock, or * a race and get two different registries started. * * The build rule also means that all beaninfo registry creations will be serialized and have * a race condition. The unfortunate part is that two independent project's registries can't be * created at same time. But that is the result of the build rule. We can't allow the builds, so * we need to stop all parallel beaninfo registry creations. * * @param nature * @param pm * * @since 1.0.0 */ protected final void doCreateRegistry(BeaninfoNature nature, IProgressMonitor pm) { pm.beginTask("", 100); //$NON-NLS-1$ try { nature.createRegistry(new SubProgressMonitor(pm, 100)); } finally { pm.done(); } } }