/******************************************************************************* * Copyright (c) 2004, 2006 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.core; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; import java.util.UUID; import org.eclipse.buckminster.core.actor.IPerformManager; import org.eclipse.buckminster.core.ctype.IComponentType; import org.eclipse.buckminster.core.ctype.IResolutionBuilder; import org.eclipse.buckminster.core.ctype.MissingBuilderException; import org.eclipse.buckminster.core.ctype.MissingComponentTypeException; import org.eclipse.buckminster.core.helpers.ShortDurationURLCache; import org.eclipse.buckminster.core.internal.actor.PerformManager; import org.eclipse.buckminster.core.materializer.IMaterializer; import org.eclipse.buckminster.core.materializer.MaterializationJob; import org.eclipse.buckminster.core.materializer.WorkspaceBindingInstallJob; import org.eclipse.buckminster.core.metadata.MetadataSynchronizer; import org.eclipse.buckminster.core.parser.IParserFactory; import org.eclipse.buckminster.core.parser.ParserFactory; import org.eclipse.buckminster.core.reader.ICatalogReader; import org.eclipse.buckminster.core.reader.IReaderType; import org.eclipse.buckminster.core.reader.MissingReaderTypeException; import org.eclipse.buckminster.core.reader.RemoteFile; import org.eclipse.buckminster.core.reader.RemoteFileCache; import org.eclipse.buckminster.core.version.IQualifierGenerator; import org.eclipse.buckminster.core.version.IVersionConverter; import org.eclipse.buckminster.core.version.MissingMaterializerException; import org.eclipse.buckminster.core.version.MissingVersionConverterException; import org.eclipse.buckminster.runtime.Buckminster; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.BuckminsterPreferences; import org.eclipse.buckminster.runtime.LogAwarePlugin; import org.eclipse.buckminster.runtime.Logger; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.ILogListener; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.ecf.core.security.IConnectContext; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.IProvisioningAgentProvider; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; /** * The main plugin class to be used in the desktop. * * @author Thomas Hallgren */ public class CorePlugin extends LogAwarePlugin { public static final String CORE_NAMESPACE = Buckminster.NAMESPACE + ".core"; //$NON-NLS-1$ public static final String ANT_TASK_GENERATORS = CORE_NAMESPACE + ".anttaskGenerators"; //$NON-NLS-1$ public static final String BOM_FILE = "buckminster.bom"; //$NON-NLS-1$ public static final String COMPONENT_TYPE_POINT = CORE_NAMESPACE + ".componentTypes"; //$NON-NLS-1$ public static final String CQUERY_FILE = "buckminster.cquery"; //$NON-NLS-1$ public static final String CSPEC_BUILDER_POINT = CORE_NAMESPACE + ".cspecBuilders"; //$NON-NLS-1$ public static final String CSPEC_FILE = "buckminster.cspec"; //$NON-NLS-1$ public static final String CSPECEXT_FILE = "buckminster.cspex"; //$NON-NLS-1$ public static final String FORCED_ACTIVATIONS_POINT = CORE_NAMESPACE + ".forcedActivations"; //$NON-NLS-1$ public static final String QUALIFIER_GENERATOR_POINT = CORE_NAMESPACE + ".qualifierGenerators"; //$NON-NLS-1$ public static final String READER_TYPE_POINT = CORE_NAMESPACE + ".readerTypes"; //$NON-NLS-1$ public static final String VERSION_CONVERTERS_POINT = CORE_NAMESPACE + ".versionConverters"; //$NON-NLS-1$ public static final String ACTORS_POINT = CORE_NAMESPACE + ".actors"; //$NON-NLS-1$ public static final String INTERNAL_ACTORS_POINT = CORE_NAMESPACE + ".internalActors"; //$NON-NLS-1$ public static final String BUCKMINSTER_PROJECT = ".buckminster"; //$NON-NLS-1$ private static CorePlugin plugin; public static CorePlugin getDefault() { return plugin; } public static String getID() { return plugin.toString(); } public static Logger getLogger() { CorePlugin corePlugin = plugin; if (corePlugin != null) return corePlugin.getBundleLogger(); return new Logger(new ILog() { @Override public void addLogListener(ILogListener listener) { } @Override public Bundle getBundle() { return null; } @Override public void log(IStatus status) { if (status == null) return; switch (status.getSeverity()) { case IStatus.ERROR: case IStatus.WARNING: System.err.println(status.getMessage()); break; default: System.out.println(status.getMessage()); } } @Override public void removeLogListener(ILogListener listener) { } }); } /** * Returns the perform manager that coordinates all perform activity in the * current workspace * * @return the perform manager for the workspace */ public static IPerformManager getPerformManager() throws CoreException { return PerformManager.getInstance(); } /** * Returns the string from the plugin's resource bundle, or 'key' if not * found. */ public static String getResourceString(String key) { ResourceBundle bundle = CorePlugin.getDefault().getResourceBundle(); try { return (bundle != null) ? bundle.getString(key) : key; } catch (MissingResourceException e) { return key; } } public static void logWarningsAndErrors(IStatus status) { logWarningsAndErrors(status, new StringBuilder(), new HashSet<String>(), 0); } private static void logWarningsAndErrors(IStatus status, StringBuilder line, Set<String> unique, int indent) { switch (status.getSeverity()) { case IStatus.CANCEL: throw new OperationCanceledException(); case IStatus.OK: break; default: line.setLength(0); if (indent > 0) { char leadIn; switch (status.getSeverity()) { case IStatus.ERROR: leadIn = 'E'; break; case IStatus.WARNING: leadIn = 'W'; break; default: leadIn = 'I'; } line.append(leadIn); for (int idx = 1; idx < indent; ++idx) line.append(' '); } String msg = status.getMessage(); if (msg != null) line.append(msg); Throwable reason = status.getException(); if (reason != null) { String reasonMsg = reason.getMessage(); if (reasonMsg == null) reasonMsg = reason.toString(); if (msg == null) line.append(reasonMsg); else if (!msg.equals(reasonMsg)) { line.append(": "); //$NON-NLS-1$ line.append(reasonMsg); } } String logMsg = line.toString(); if (unique.add(logMsg)) getLogger().log(status.getSeverity(), logMsg); HashSet<String> childUnique = new HashSet<String>(); for (IStatus child : status.getChildren()) logWarningsAndErrors(child, line, childUnique, indent + 2); } } private final RemoteFileCache remoteFileCache = new RemoteFileCache(30000, "bm-remote", ".cache", null); //$NON-NLS-1$ //$NON-NLS-2$ private ResourceBundle resourceBundle; private final Map<String, Map<String, Object>> singletonExtensionCache = new HashMap<String, Map<String, Object>>(); private final ShortDurationURLCache urlCache = new ShortDurationURLCache(); private WorkspaceJob updatePrefsJob; private IProvisioningAgent resolverAgent; /** * The constructor. */ public CorePlugin() { super(); plugin = this; } /** * Clear the remote file cache */ public void clearRemoteFileCache() { remoteFileCache.clear(); } /** * Clear the remote file cache */ public void clearURLCache() { urlCache.clear(); } /** * Generate a new UUID. We keep this in a separate method in case we'd like * to change to type 1 at some point. For now, a random type 4 UUID is used. * * @return A randomly generated type 4 UUID. */ public UUID createUUID() { return UUID.randomUUID(); } /** * Returns the special project <code>.buckminster</code> that acts as a * placeholder for all artifacts that are not otherwise mapped into the * workspace. The project will be created if the * <code>createIfMissing</code> flag is <code>true</code> and opened if it * was not so already. * * @param createIfMissing * Set to true if the project should be created when its missing. * @param monitor * The progress monitor * @return The .buckminster project or <code>null</code> if its missing and * the <code>createIfMissing</code> parameter was false. */ public IProject getBuckminsterProject(boolean createIfMissing, IProgressMonitor monitor) throws CoreException { monitor = MonitorUtils.ensureNotNull(monitor); IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); IProject bmProject = wsRoot.getProject(BUCKMINSTER_PROJECT); boolean exists = bmProject.exists(); if (exists) { if (!bmProject.isOpen()) bmProject.open(monitor); else MonitorUtils.complete(monitor); return bmProject; } if (!createIfMissing) return null; monitor.beginTask(null, 2000); monitor.subTask("Creating .buckminster project"); //$NON-NLS-1$ IPath path = getBuckminsterProjectLocation(); IPath defaultLocation = wsRoot.getLocation().append(CorePlugin.BUCKMINSTER_PROJECT); if (!path.equals(defaultLocation)) { IProjectDescription desc = ResourcesPlugin.getWorkspace().newProjectDescription(BUCKMINSTER_PROJECT); desc.setLocation(path); bmProject.create(desc, MonitorUtils.subMonitor(monitor, 1000)); } else bmProject.create(MonitorUtils.subMonitor(monitor, 1000)); bmProject.open(MonitorUtils.subMonitor(monitor, 1000)); monitor.done(); return bmProject; } /** * Returns the absolute path in the local filesystem for the * <code>.buckminster</code> project The project need not exist. * * @return The location of the .buckminster project. */ public IPath getBuckminsterProjectLocation() { IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); IProject bmProject = wsRoot.getProject(BUCKMINSTER_PROJECT); if (bmProject.exists()) return bmProject.getLocation(); IPath path = BuckminsterPreferences.getBuckminterProjectContents(); if (path == null) path = wsRoot.getLocation().append(CorePlugin.BUCKMINSTER_PROJECT); return path; } public IComponentType getComponentType(String componentType) throws CoreException { IComponentType cType = getExecutableExtension(IComponentType.class, COMPONENT_TYPE_POINT, componentType, true); if (cType != null) return cType; throw new MissingComponentTypeException(componentType); } /** * Obtain a executable extension from the platform extension registry. A * cache is utilized to ensure a singleton pattern. * * @param instanceClass * The class of the executable extension. * @param extensionPoint * The extension point id. * @param instanceId * The instance id. * @param useSingelton * Set to true if the instance should be cached and returned on * subsequent calls. * @return A singleton instance that corresponds to the extension. * @throws CoreException */ public <T extends Object> T getExecutableExtension(Class<T> instanceClass, String extensionPoint, String instanceId, boolean useSingleton) throws CoreException { Object extension = null; Map<String, Object> singletonCache = null; if (useSingleton) { singletonCache = singletonExtensionCache.get(extensionPoint); if (singletonCache != null) { extension = singletonCache.get(instanceId); if (extension != null) return instanceClass.cast(extension); } } IExtensionRegistry exReg = Platform.getExtensionRegistry(); IConfigurationElement[] elems = exReg.getConfigurationElementsFor(extensionPoint); for (IConfigurationElement elem : elems) { if (elem.getAttribute("id").equals(instanceId)) //$NON-NLS-1$ { extension = elem.createExecutableExtension("class"); //$NON-NLS-1$ if (useSingleton) { if (singletonCache == null) { singletonCache = new HashMap<String, Object>(); singletonExtensionCache.put(extensionPoint, singletonCache); } singletonCache.put(instanceId, extension); } return instanceClass.cast(extension); } } return null; } /** * Returns the known ids for the given extension point * * @param extensionPoint * @return */ public String[] getExtensionIds(String extensionPoint) { IExtensionRegistry exReg = Platform.getExtensionRegistry(); IConfigurationElement[] elems = exReg.getConfigurationElementsFor(extensionPoint); int idx = elems.length; String[] ids = new String[idx]; while (--idx >= 0) ids[idx] = elems[idx].getAttribute("id"); //$NON-NLS-1$ return ids; } public IMaterializer getMaterializer(String materializerId) throws CoreException { if (materializerId == null) materializerId = IMaterializer.WORKSPACE; IMaterializer mat = getExecutableExtension(IMaterializer.class, IMaterializer.MATERIALIZERS_POINT, materializerId, true); if (mat != null) return mat; throw new MissingMaterializerException(materializerId); } public IParserFactory getParserFactory() { return ParserFactory.getDefault(); } public IQualifierGenerator getQualifierGenerator(String qualifierGenerator) throws CoreException { IQualifierGenerator vm = getExecutableExtension(IQualifierGenerator.class, QUALIFIER_GENERATOR_POINT, qualifierGenerator, true); if (vm != null) return vm; throw BuckminsterException.fromMessage(NLS.bind(Messages.Missing_qualifier_generator_for_id_0, qualifierGenerator)); } public IReaderType getReaderType(String readerType) throws CoreException { IReaderType vm = getExecutableExtension(IReaderType.class, READER_TYPE_POINT, readerType, true); if (vm != null) return vm; throw new MissingReaderTypeException(readerType); } /** * Locates the extension {@link #CSPEC_BUILDER_POINT} and creates a * resolution builder. * * @param id * The id of the desired builder * @return A resolution builder. * @throws CoreException * if no builder can be found with the given id or if something * goes wrong when instantiating it. */ public IResolutionBuilder getResolutionBuilder(String id) throws CoreException { IResolutionBuilder builder = getExecutableExtension(IResolutionBuilder.class, CSPEC_BUILDER_POINT, id, false); if (builder != null) return builder; throw new MissingBuilderException(id); } public synchronized IProvisioningAgent getResolverAgent() throws CoreException { if (resolverAgent == null) { Buckminster bucky = Buckminster.getDefault(); IProvisioningAgentProvider agentProvider = bucky.getService(IProvisioningAgentProvider.class); try { File agentLocation = getResolverAgentLocation(); resolverAgent = agentProvider.createAgent(agentLocation.toURI()); } finally { bucky.ungetService(agentProvider); } } return resolverAgent; } public File getResolverAgentLocation() { return getStateLocation().append("p2.resolver").toFile(); //$NON-NLS-1$ } /** * Returns the plugin's resource bundle, */ public ResourceBundle getResourceBundle() { try { if (resourceBundle == null) resourceBundle = ResourceBundle.getBundle(CORE_NAMESPACE + ".CorePluginResources"); //$NON-NLS-1$ } catch (MissingResourceException x) { resourceBundle = null; } return resourceBundle; } public IVersionConverter getVersionConverter(String versionConverter) throws CoreException { if (versionConverter == null) versionConverter = "tag"; //$NON-NLS-1$ IVersionConverter vc = getExecutableExtension(IVersionConverter.class, VERSION_CONVERTERS_POINT, versionConverter, false); if (vc != null) return vc; throw new MissingVersionConverterException(versionConverter); } /** * Opens a remote file using the short duration cache maintained by this * plugin. * * @param remoteFile * the file to open * @return An opened stream to the cached entry. * @throws IOException */ public InputStream openCachedRemoteFile(ICatalogReader reader, String fileName, IProgressMonitor monitor) throws IOException, CoreException { return remoteFileCache.openRemoteFile(new RemoteFile(reader, fileName), monitor); } /** * Opens a url using the short duration cache maintained by this plugin. * * @param url * @return input stream for the url * @throws IOException */ public InputStream openCachedURL(URL url, IConnectContext cctx, IProgressMonitor monitor) throws IOException, CoreException { return urlCache.openURL(url, cctx, monitor); } /** * This method is called upon plug-in activation */ @Override public void start(BundleContext context) throws Exception { super.start(context); Job startJob = new Job("Core plugin starter") //$NON-NLS-1$ { @Override public IStatus run(IProgressMonitor monitor) { IExtensionRegistry exReg = Platform.getExtensionRegistry(); if (exReg == null) return Status.OK_STATUS; // We died before we got the chance MetadataSynchronizer.setUp(); MaterializationJob.setUp(); IConfigurationElement[] forcedActivations = exReg.getConfigurationElementsFor(FORCED_ACTIVATIONS_POINT); monitor.beginTask(null, forcedActivations.length); for (IConfigurationElement elem : forcedActivations) { String pluginId = elem.getAttribute("pluginId"); //$NON-NLS-1$ try { Bundle bundle = Platform.getBundle(pluginId); bundle.loadClass(elem.getAttribute("class")); //$NON-NLS-1$ } catch (Exception e) { getLogger().warning(e, NLS.bind(Messages.Unable_to_activate_bundle_0, pluginId)); } monitor.worked(1); } monitor.done(); WorkspaceBindingInstallJob.start(); return Status.OK_STATUS; } }; startJob.schedule(100); } @Override public void stop(BundleContext context) throws Exception { if (resolverAgent != null) resolverAgent.stop(); super.stop(context); } public synchronized void stopAllJobs() throws Exception { if (updatePrefsJob != null) { updatePrefsJob.cancel(); updatePrefsJob.join(); updatePrefsJob = null; } } }