/* Copyright (C) 2009 Mobile Sorcery AB This program is free software; you can redistribute it and/or modify it under the terms of the Eclipse Public License v1.0. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for more details. You should have received a copy of the Eclipse Public License v1.0 along with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html */ package com.mobilesorcery.sdk.core; import java.io.FileInputStream; import java.io.InputStream; import java.security.SecureRandom; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.TreeSet; import java.util.UUID; import java.util.regex.Pattern; import javax.crypto.spec.PBEKeySpec; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import com.mobilesorcery.sdk.core.build.BuildSequence; import com.mobilesorcery.sdk.core.build.BundleBuildStep; import com.mobilesorcery.sdk.core.build.CommandLineBuildStep; import com.mobilesorcery.sdk.core.build.CompileBuildStep; import com.mobilesorcery.sdk.core.build.CopyBuildResultBuildStep; import com.mobilesorcery.sdk.core.build.IBuildStepFactory; import com.mobilesorcery.sdk.core.build.IBuildStepFactoryExtension; import com.mobilesorcery.sdk.core.build.LinkBuildStep; import com.mobilesorcery.sdk.core.build.NativeLibBuildStep; import com.mobilesorcery.sdk.core.build.PackBuildStep; import com.mobilesorcery.sdk.core.build.ResourceBuildStep; import com.mobilesorcery.sdk.core.launch.AutomaticEmulatorLauncher; import com.mobilesorcery.sdk.core.launch.IEmulatorLauncher; import com.mobilesorcery.sdk.core.launch.MoReLauncher; import com.mobilesorcery.sdk.core.memory.LowMemoryManager; import com.mobilesorcery.sdk.core.security.IApplicationPermissions; import com.mobilesorcery.sdk.core.stats.Stats; import com.mobilesorcery.sdk.internal.ErrorPackager; import com.mobilesorcery.sdk.internal.HeadlessUpdater; import com.mobilesorcery.sdk.internal.PID; import com.mobilesorcery.sdk.internal.PROCESS; import com.mobilesorcery.sdk.internal.PackagerProxy; import com.mobilesorcery.sdk.internal.PropertyInitializerProxy; import com.mobilesorcery.sdk.internal.RebuildListener; import com.mobilesorcery.sdk.internal.ReindexListener; import com.mobilesorcery.sdk.internal.SecurePasswordProvider; import com.mobilesorcery.sdk.internal.SecureProperties; import com.mobilesorcery.sdk.internal.dependencies.DependencyManager; import com.mobilesorcery.sdk.internal.launch.EmulatorLauncherProxy; import com.mobilesorcery.sdk.internal.security.ApplicationPermissions; import com.mobilesorcery.sdk.lib.JNALibInitializer; import com.mobilesorcery.sdk.profiles.IProfile; import com.mobilesorcery.sdk.profiles.filter.DeviceFilterFactoryProxy; import com.mobilesorcery.sdk.profiles.filter.IDeviceFilterFactory; import com.mobilesorcery.sdk.profiles.filter.elementfactories.ConstantFilterFactory; import com.mobilesorcery.sdk.profiles.filter.elementfactories.DeviceCapabilitiesFilterFactory; import com.mobilesorcery.sdk.profiles.filter.elementfactories.FeatureFilterFactory; import com.mobilesorcery.sdk.profiles.filter.elementfactories.ProfileFilterFactory; import com.mobilesorcery.sdk.profiles.filter.elementfactories.VendorFilterFactory; /** * The activator class controls the plug-in life cycle */ public class CoreMoSyncPlugin extends AbstractUIPlugin implements IPropertyChangeListener, IResourceChangeListener { // The plug-in ID public static final String PLUGIN_ID = "com.mobilesorcery.sdk.core"; /** * A non-UI console id. */ public static final String LOG_CONSOLE_NAME = "@@log"; private static final String WORKSPACE_TOKEN_PREF = PLUGIN_ID + ".w.s.token"; private static final String PREFERRED_LAUNCER_PREF_PREFIX = PLUGIN_ID + "preferred.launcher."; // Days since Oct 15, 1528 until Jan 1, 1970 private static final long DCE_OFFSET = 141427 * 86400; // The shared instance private static CoreMoSyncPlugin plugin; private static LowMemoryManager lowMemoryManager; private static ISavePolicy savePolicy; private final ArrayList<Pattern> runtimePatterns = new ArrayList<Pattern>(); private final ArrayList<Pattern> platformPatterns = new ArrayList<Pattern>(); private final ArrayList<IPackager> packagers = new ArrayList<IPackager>(); private Map<String, Map<String, IPropertyInitializer>> propertyInitializers = new HashMap<String, Map<String, IPropertyInitializer>>(); private final HashMap<String, IEmulatorLauncher> launchers = new HashMap<String, IEmulatorLauncher>(); private DependencyManager<IProject> projectDependencyManager; private Properties panicMessages = new Properties(); private ReindexListener reindexListener; private Integer[] sortedPanicErrorCodes; private boolean isHeadless = false; private HashMap<String, IDeviceFilterFactory> factories; private boolean updaterInitialized; private IUpdater updater; private IProvider<IProcessConsole, String> ideProcessConsoleProvider; private EmulatorProcessManager emulatorProcessManager; private String[] buildConfigurationTypes; private final HashMap<String, Integer> logCounts = new HashMap<String, Integer>(); private ISecurePropertyOwner secureProperties; private final SecurePasswordProvider passwordProvider = new SecurePasswordProvider(); private String workspaceToken; private SecureRandom secureRnd; private HashMap<String, IBuildStepFactoryExtension> buildStepExtensions = null; private List<String> buildStepFactoryIds; private boolean nativeLibsInited = false; /** * The constructor */ public CoreMoSyncPlugin() { } @Override public void start(BundleContext context) throws Exception { super.start(context); plugin = this; aboutBoxHack(); initReIndexerListener(); initRebuildListener(); initPackagers(); initDeviceFilterFactories(); initPanicErrorMessages(); initPropertyInitializers(); initGlobalDependencyManager(); initEmulatorProcessManager(); installResourceListener(); initBuildConfigurationTypes(); initLaunchers(); initSecureProperties(); initWorkspaceToken(); getPreferenceStore().addPropertyChangeListener(this); initializeOnSeparateThread(); } private void aboutBoxHack() { // The about box makes use of system properties; let's set a few. String mosyncVersion = MoSyncTool.getDefault().getVersionInfo(MoSyncTool.BINARY_VERSION); String buildDate = MoSyncTool.getDefault().getVersionInfo(MoSyncTool.BUILD_DATE); String mainGitHash = MoSyncTool.getDefault().getVersionInfo(MoSyncTool.MOSYNC_GIT_HASH); String eclipseGitHash = MoSyncTool.getDefault().getVersionInfo(MoSyncTool.ECLIPSE_GIT_HASH); System.setProperty("MOSYNC_VERSION", mosyncVersion); System.setProperty("MOSYNC_BUILD_DATE", "Build date: " + buildDate); System.setProperty("MOSYNC_MAIN_GIT_HASH", mainGitHash); System.setProperty("MOSYNC_ECLIPSE_GIT_HASH", eclipseGitHash); } private void initStats() { Stats.getStats().start(); } private void initSecureProperties() { secureProperties = new SecureProperties(new PreferenceStorePropertyOwner(getPreferenceStore()), getPasswordProvider(), null); } private void initLaunchers() { // Default and auto always present this.launchers.put(AutomaticEmulatorLauncher.ID, new AutomaticEmulatorLauncher()); this.launchers.put(MoReLauncher.ID, new MoReLauncher()); IConfigurationElement[] launchers = Platform.getExtensionRegistry().getConfigurationElementsFor(IEmulatorLauncher.EXTENSION_POINT_ID); for (int i = 0; i < launchers.length; i++) { IConfigurationElement launcher = launchers[i]; String id = launcher.getAttribute("id"); this.launchers.put(id, new EmulatorLauncherProxy(launcher)); } } private void initBuildConfigurationTypes() { IConfigurationElement[] types = Platform.getExtensionRegistry().getConfigurationElementsFor( BuildConfiguration.TYPE_EXTENSION_POINT); ArrayList<String> buildConfigurationTypes = new ArrayList<String>(); // Add defaults buildConfigurationTypes.add(IBuildConfiguration.RELEASE_TYPE); buildConfigurationTypes.add(IBuildConfiguration.DEBUG_TYPE); // Add extensions for (int i = 0; i < types.length; i++) { String typeId = types[i].getAttribute("id"); if (typeId != null) { buildConfigurationTypes.add(typeId); } } this.buildConfigurationTypes = buildConfigurationTypes.toArray(new String[0]); } void initializeOnSeparateThread() { // I think we should move this to the UI plugin! Thread initializerThread = new Thread(new Runnable() { @Override public void run() { initStats(); } }, "Initializer"); initializerThread.setDaemon(true); initializerThread.start(); } /** * Returns whether this app is running in headless mode. * @return */ public static boolean isHeadless() { return plugin.isHeadless; } /** * Sets this app to headless/non-headless mode. * Please note that this will trigger a bundle activation, * so if you want to make sure headless is set before that * use <code>System.setProperty("com.mobilesorcery.headless", true")</code> * @param isHeadless */ public static void setHeadless(boolean isHeadless) { plugin.isHeadless = isHeadless; if (isHeadless) { getDefault().getLog().log(new Status(IStatus.INFO, PLUGIN_ID, "Entering headless mode")); } } private void initGlobalDependencyManager() { // Currently, all workspaces share this guy -- fixme later. this.projectDependencyManager = new DependencyManager<IProject>(); } private void initRebuildListener() { MoSyncProject.addGlobalPropertyChangeListener(new RebuildListener()); } @Override public void stop(BundleContext context) throws Exception { // Must be here, before nulling the plugin Stats.getStats().stop(); plugin = null; projectDependencyManager = null; disposeUpdater(); MoSyncProject.removeGlobalPropertyChangeListener(reindexListener); deinstallResourceListener(); super.stop(context); } private void initPropertyInitializers() { IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor( IPropertyInitializerDelegate.EXTENSION_POINT); propertyInitializers = new HashMap<String, Map<String, IPropertyInitializer>>(); for (int i = 0; i < elements.length; i++) { String context = PropertyInitializerProxy.getContext(elements[i]); String prefix = PropertyInitializerProxy.getPrefix(elements[i]); if (context != null && prefix != null) { Map<String, IPropertyInitializer> prefixMap = propertyInitializers.get(context); if (prefixMap == null) { prefixMap = new HashMap<String, IPropertyInitializer>(); propertyInitializers.put(context, prefixMap); } prefixMap.put(prefix, new PropertyInitializerProxy(elements[i])); } } } /** * <p> * From the registered <code>IPropertyInitializerDelegate</code>s, returns * the default value for <code>key</code>, where <code>key</code> has the * format <code>prefix:subkey</code>. * </p> * <p> * <code>IPropertyInitializerDelegate</code>s are always registered with * context and prefix, which are used for lookup. * </p> * <p> * The context is the same as is returned from the <code>getContext()</code> * method in <code>IPropertyOwner</code>. * * @param owner * @param key * @return May return <code>null</code> */ public String getDefaultValue(IPropertyOwner owner, String key) { Map<String, IPropertyInitializer> prefixMap = propertyInitializers.get(owner.getContext()); if (prefixMap != null) { String[] prefixAndSubkey = key.split(":", 2); if (prefixAndSubkey.length == 2) { IPropertyInitializer initializer = prefixMap.get(prefixAndSubkey[0]); if (initializer != null) { return initializer.getDefaultValue(owner, key); } } } return null; } private void initReIndexerListener() { reindexListener = new ReindexListener(); MoSyncProject.addGlobalPropertyChangeListener(new ReindexListener()); } private void initEmulatorProcessManager() { this.emulatorProcessManager = new EmulatorProcessManager(); } private void initPanicErrorMessages() { try { panicMessages = new Properties(); InputStream messagesStream = new FileInputStream(MoSyncTool.getDefault().getMoSyncHome(). append("eclipse/paniccodes.properties").toFile()); try { panicMessages.load(messagesStream); } finally { Util.safeClose(messagesStream); } } catch (Exception e) { // Just ignore. getLog().log(new Status(IStatus.WARNING, PLUGIN_ID, "Could not initialize panic messages", e)); } TreeSet<Integer> result = new TreeSet<Integer>(); for (Enumeration errorCodes = panicMessages.keys(); errorCodes.hasMoreElements(); ) { try { String errorCode = (String) errorCodes.nextElement(); int errorCodeValue = Integer.parseInt(errorCode); result.add(errorCodeValue); } catch (Exception e) { // Just ignore. } } sortedPanicErrorCodes = result.toArray(new Integer[result.size()]); } /** * Returns the shared instance * * @return the shared instance */ public static CoreMoSyncPlugin getDefault() { return plugin; } /** * Returns an image descriptor for the image file at the given plug-in * relative path * * @param path * the path * @return the image descriptor */ public static ImageDescriptor getImageDescriptor(String path) { return imageDescriptorFromPlugin(PLUGIN_ID, path); } public void log(Throwable e) { getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage(), e)); } private synchronized void initNativeLibs() { if (nativeLibsInited) { return; } nativeLibsInited = true; try { JNALibInitializer.init(this.getBundle(), "libpipe"); @SuppressWarnings("unused") PROCESS dummy = PROCESS.INSTANCE; // Just to execute the .clinit. JNALibInitializer.init(this.getBundle(), "libpid2"); if (isDebugging()) { trace("Process id: " + getPid()); } } catch (Throwable t) { log(t); t.printStackTrace(); } } private void initPackagers() { IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor(IPackager.EXTENSION_POINT); for (int i = 0; i < elements.length; i++) { try { String runtime = elements[i].getAttribute(PackagerProxy.RUNTIME_PATTERN); String platform = elements[i].getAttribute(PackagerProxy.PLATFORM_PATTERN); Pattern runtimePattern = runtime == null ? null : Pattern.compile(runtime); Pattern platformPattern = platform == null ? null : Pattern.compile(platform); runtimePatterns.add(runtimePattern); platformPatterns.add(platformPattern); packagers.add(new PackagerProxy(elements[i])); } catch (Exception e) { CoreMoSyncPlugin.getDefault().log(e); } } } /** * Returns the packager for a specific project/platform. * @param project The profile type * @param profile The profile to package for * @return A non-null packager. If no packager is found, * a default <code>ErrorPackager</code> is returned * @see ErrorPackager */ public IPackager getPackager(int profileType, IProfile profile) { String runtime = profile.getRuntime(); String platform = profile.getVendor().getName(); IPackager packager = null; if (profileType == MoSyncTool.DEFAULT_PROFILE_TYPE) { packager = matchPackager(platformPatterns, platform); } if (packager == null) { packager = matchPackager(runtimePatterns, runtime); } if (packager == null) { packager = ErrorPackager.getDefault(); } return packager; } private IPackager matchPackager(List<Pattern> patterns, String matchingCriteria) { for (int i = 0; i < patterns.size(); i++) { Pattern pattern = patterns.get(i); if (pattern != null && pattern.matcher(matchingCriteria).matches()) { return packagers.get(i); } } return null; } /** * Returns an (unmodifiable) list of all available packagers * @return */ public List<IPackager> getPackagers() { return Collections.unmodifiableList(packagers); } /** * Returns a packager with a specific id. * @param id * @return */ public IPackager getPackagerById(String id) { for (IPackager packager : packagers) { if (Util.equals(packager.getId(), id)) { return packager; } } return null; } /** * Returns a sorted list of all panic error codes. * @return */ public Integer[] getAllPanicErrorCodes() { return sortedPanicErrorCodes; } /** * Returns the panic message corresponding to <code>errcode</code> * @param errcode * @return */ public String getPanicMessage(int errcode) { return panicMessages.getProperty(Integer.toString(errcode)); } /** * @deprecated Do we really need this? It is never used outside * the builder + recalculated every time... * @return */ @Deprecated public DependencyManager<IProject> getProjectDependencyManager() { return getProjectDependencyManager(ResourcesPlugin.getWorkspace()); } /** * @deprecated Do we really need this? It is never used outside * the builder + recalculated every time... * @param ws * @return */ @Deprecated public DependencyManager<IProject> getProjectDependencyManager(IWorkspace ws) { return projectDependencyManager; } /** * Returns the Eclipse OS Process ID. * @return */ public String getPid() { initNativeLibs(); return "" + PID.INSTANCE.pid(); } public IProcessUtil getProcessUtil() { initNativeLibs(); return PROCESS.INSTANCE; } /** * <p>Outputs a trace message.</p> * <p>Please use this pattern: * <blockquote><code> * if (CoreMoSyncPlugin.getDefault().isDebugging()) { * trace("A trace message"); * } * </code></blockquote> * </p> * <p>Long messages will be truncated.</p> * @param msg */ public static void trace(Object msg) { System.out.println(Util.truncate("" + msg, null, 1024)); } /** * <p>Outputs a trace message.</p> * <p>The arguments match those of <code>MessageFormat.format</code>.</p> * @see {@link CoreMoSyncPlugin#trace(Object)}; */ public static void trace(String msg, Object... args) { trace(MessageFormat.format(msg, args)); } private void initDeviceFilterFactories() { factories = new HashMap<String, IDeviceFilterFactory>(); // We'll just add some of them explicitly factories.put(ConstantFilterFactory.ID, new ConstantFilterFactory()); factories.put(VendorFilterFactory.ID, new VendorFilterFactory()); factories.put(FeatureFilterFactory.ID, new FeatureFilterFactory()); factories.put(ProfileFilterFactory.ID, new ProfileFilterFactory()); factories.put(DeviceCapabilitiesFilterFactory.ID, new DeviceCapabilitiesFilterFactory()); IConfigurationElement[] factoryCEs = Platform.getExtensionRegistry().getConfigurationElementsFor(PLUGIN_ID + ".filter.factories"); for (int i = 0; i < factoryCEs.length; i++) { IConfigurationElement factoryCE = factoryCEs[i]; String id = factoryCE.getAttribute("id"); DeviceFilterFactoryProxy factory = new DeviceFilterFactoryProxy(factoryCE); registerDeviceFilterFactory(id, factory); } } private void registerDeviceFilterFactory(String id, IDeviceFilterFactory factory) { if (factories.containsKey(id)) { throw new IllegalStateException("Id already used"); } factories.put(id, factory); } private void installResourceListener() { ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_BUILD | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.POST_CHANGE); } private void deinstallResourceListener() { ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } /** * <p>Returns an <code>IDeviceFilterFactory</code>.</p> * <p>Examples of <code>IDeviceFilterFactories</code> are * <code>ConstantFilterFactory</code> and <code>VenderFilterFactory</code>. * @param factoryId * @return */ public IDeviceFilterFactory getDeviceFilterFactory(String factoryId) { if (factoryId == null) { return null; } // Kind of an IElementFactory, but without the UI deps. return factories.get(factoryId); } /** * Creates an {@link IProcessConsole} to be used for output of command line tools, * build plugins, etc. * @param consoleName * @return An {@link IProcessConsole}. If running in headless mode or if the * {@code consoleName} is set to {@link #LOG_CONSOLE_NAME}, a non-UI console is * returned. */ public IProcessConsole createConsole(String consoleName) { if (isHeadless || LOG_CONSOLE_NAME.equals(consoleName)) { return new LogProcessConsole(consoleName); } else { return ideProcessConsoleProvider == null ? new LogProcessConsole(consoleName) : ideProcessConsoleProvider.get(consoleName); } } /** * For a given launcher id, return the corresponding {@link IEmulatorLauncher}. * @param launcherId * @return */ public IEmulatorLauncher getEmulatorLauncher(String launcherId) { return launchers.get(launcherId); } /** * Returns the preferred launcher for a given packager. * @param packager * @return {@code null} if no preferred launcher */ public IEmulatorLauncher getPreferredLauncher(String packager) { IPreferenceStore store = getPreferenceStore(); String launcherId = store.getString(PREFERRED_LAUNCER_PREF_PREFIX + packager); return getEmulatorLauncher(launcherId); } /** * Sets the preferred launcher for a given packager. * @param packager * @param launcherId {@code null} if no preferred launcher should be set */ public void setPreferredLauncher(String packager, String launcherId) { IPreferenceStore store = getPreferenceStore(); String pref = PREFERRED_LAUNCER_PREF_PREFIX + packager; if (launcherId == null) { store.setToDefault(pref); } else { store.setValue(pref, launcherId); } } public Set<String> getEmulatorLauncherIds() { return Collections.unmodifiableSet(launchers.keySet()); } public IBuildStepFactory createBuildStepFactory(String id) { // The default ones. if (CompileBuildStep.ID.equals(id)) { return new CompileBuildStep.Factory(); } else if (ResourceBuildStep.ID.equals(id)) { return new ResourceBuildStep.Factory(); } else if (LinkBuildStep.ID.equals(id)) { return new LinkBuildStep.Factory(); } else if (PackBuildStep.ID.equals(id)) { return new PackBuildStep.Factory(); } else if (CommandLineBuildStep.ID.equals(id)) { return new CommandLineBuildStep.Factory(); } else if (BundleBuildStep.ID.equals(id)) { return new BundleBuildStep.Factory(); } else if (CopyBuildResultBuildStep.ID.equals(id)) { return new CopyBuildResultBuildStep.Factory(); } else if (NativeLibBuildStep.ID.equals(id)) { return new NativeLibBuildStep.Factory(); } IBuildStepFactoryExtension extension = getBuildStepFactoryExtension(id); if (extension != null) { return extension.createFactory(); } return null; } public IBuildStepFactoryExtension getBuildStepFactoryExtension(String id) { if (buildStepExtensions == null) { buildStepExtensions = new HashMap<String, IBuildStepFactoryExtension>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); IConfigurationElement[] elements = registry .getConfigurationElementsFor(IBuildStepFactoryExtension.EXTENSION_ID); for (IConfigurationElement element : elements) { try { String extId = element.getAttribute("id"); IBuildStepFactoryExtension ext = (IBuildStepFactoryExtension) element.createExecutableExtension("implementation"); buildStepExtensions.put(extId, ext); } catch (CoreException e) { CoreMoSyncPlugin.getDefault().log(e); } } } return buildStepExtensions.get(id); } /** * Returns a mutable list of all build step factories. * @return */ public List<String> getBuildStepFactories() { if (buildStepFactoryIds == null) { ArrayList<String> result = new ArrayList<String>(); result.add(CompileBuildStep.ID); result.add(ResourceBuildStep.ID); result.add(LinkBuildStep.ID); result.add(PackBuildStep.ID); result.add(CommandLineBuildStep.ID); result.add(BundleBuildStep.ID); result.add(CopyBuildResultBuildStep.ID); getBuildStepFactoryExtension(""); // Just to init. result.addAll(buildStepExtensions.keySet()); buildStepFactoryIds = Collections.unmodifiableList(result); } return buildStepFactoryIds; } public IUpdater getUpdater() { if (isHeadless) { if (isDebugging()) { trace("Headless build: update checks suppressed"); } return HeadlessUpdater.getInstance(); } else if (!updaterInitialized) { IExtensionRegistry registry = Platform.getExtensionRegistry(); IConfigurationElement[] elements = registry .getConfigurationElementsFor("com.mobilesorcery.sdk.updater"); if (elements.length > 0) { try { updater = (IUpdater) elements[0] .createExecutableExtension("implementation"); } catch (CoreException e) { getLog().log(e.getStatus()); } } updaterInitialized = true; } return updater; } private void disposeUpdater() { if (updater != null) { updater.dispose(); } } public void checkAutoUpdate() { Thread t = new Thread(new Runnable() { @Override public void run() { internalCheckAutoUpdate(); } }); t.setName("Auto update"); t.start(); } private synchronized void internalCheckAutoUpdate() { if (isHeadless) { return; } String[] args = Platform.getApplicationArgs(); if (suppressUpdating(args)) { return; } IUpdater updater = getUpdater(); if (updater != null) { updater.update(false); } } private boolean suppressUpdating(String[] args) { for (int i = 0; i < args.length; i++) { if ("-suppress-updates".equals(args[i])) { return true; } } return false; } /** * <p>Returns the (single) emulator process manager</p> * @return */ public EmulatorProcessManager getEmulatorProcessManager() { return emulatorProcessManager; } /** * INTERNAL: Clients should not call this method. */ public void setIDEProcessConsoleProvider(IProvider<IProcessConsole, String> ideProcessConsoleProvider) { // I'm lazy - Instead of extension points... this.ideProcessConsoleProvider = ideProcessConsoleProvider; } @Override public void propertyChange(PropertyChangeEvent event) { if (MoSyncTool.MOSYNC_HOME_PREF.equals(event.getProperty()) || MoSyncTool.MO_SYNC_HOME_FROM_ENV_PREF.equals(event.getProperty())) { initPanicErrorMessages(); } } /** * Tries to derive a mosync project from whatever object is passed * as the <code>receiver</code>; this method will accept <code>List</code>s, * <code>IAdaptable</code>s, <code>IResource</code>s, as well as <code>IStructuredSelection</code>s * and then if the project * associated with these is compatible with a MoSyncProject, return that project. */ // Should it be here? public MoSyncProject extractProject(Object receiver) { if (receiver instanceof IStructuredSelection) { return extractProject(((IStructuredSelection) receiver).toList()); } if (receiver instanceof IAdaptable) { receiver = ((IAdaptable) receiver).getAdapter(IResource.class); } if (receiver instanceof Collection) { if (((Collection)(receiver)).size() == 0) { return null; } return extractProject(((Collection)receiver).iterator().next()); } if(receiver == null) { return null; } if (receiver instanceof IResource) { IProject project = ((IResource)receiver).getProject(); try { return MoSyncNature.isCompatible(project) ? MoSyncProject.create(project) : null; } catch (CoreException e) { return null; } } return null; } @Override public void resourceChanged(IResourceChangeEvent event) { int eventType = event.getType(); if (eventType == IResourceChangeEvent.PRE_DELETE || eventType == IResourceChangeEvent.PRE_CLOSE) { IResource resource = event.getResource(); IProject project = (resource != null && resource.getType() == IResource.PROJECT) ? (IProject) resource : null; MoSyncProject mosyncProject = MoSyncProject.create(project); if (mosyncProject != null) { // So we do not keep any old references to this project mosyncProject.dispose(); } } else if (eventType == IResourceChangeEvent.PRE_BUILD && event.getBuildKind() != IncrementalProjectBuilder.CLEAN_BUILD && event.getBuildKind() != IncrementalProjectBuilder.AUTO_BUILD) { Object source = event.getSource(); ArrayList<IResource> mosyncProjects = new ArrayList<IResource>(); IProject[] projects = null; if (source instanceof IWorkspace) { projects = ((IWorkspace) source).getRoot().getProjects(); } else if (source instanceof IProject) { projects = new IProject[] { (IProject) source }; } for (int i = 0; projects != null && i < projects.length; i++) { IProject project = projects[i]; try { if (MoSyncNature.isCompatible(project)) { mosyncProjects.add(projects[i]); } } catch (CoreException e) { CoreMoSyncPlugin.getDefault().log(e); } } IJobManager jm = Job.getJobManager(); Job currentJob = jm.currentJob(); if (!MoSyncBuilder.saveAllEditors(mosyncProjects)) { // If this thread is a build job, then cancel. // TODO: Could this somewhere or some day cease to work! if (currentJob != null) { currentJob.cancel(); } } } else { Collection<MoSyncProject> projects = extractProjectsToReinit(event); for (MoSyncProject project : projects) { project.reinit(true); } } } public Collection<MoSyncProject> extractProjectsToReinit(IResourceChangeEvent event) { final HashSet<MoSyncProject> result = new HashSet<MoSyncProject>(); boolean isContentChange = event.getDelta() != null && (event.getDelta().getFlags() & IResourceDelta.CONTENT) != 0; boolean isFileResource = event.getResource() != null && event.getResource().getType() == IResource.FILE; if (event.getType() == IResourceChangeEvent.POST_CHANGE && isContentChange && isFileResource) { try { event.getDelta().accept(new IResourceDeltaVisitor() { @Override public boolean visit(IResourceDelta delta) throws CoreException { IResource resource = delta.getResource(); if (resource != null) { String name = resource.getName(); if (MoSyncProject.MOSYNC_PROJECT_META_DATA_FILENAME.equals(name) || MoSyncProject.MOSYNC_PROJECT_META_DATA_FILENAME.equals(name)) { MoSyncProject mosyncProject = MoSyncProject.create(resource.getProject()); if (mosyncProject != null) { result.add(mosyncProject); // And for good measure... BuildSequence.clearCache(mosyncProject); } } } return true; } }); } catch (CoreException e) { CoreMoSyncPlugin.getDefault().log(e); } } return result; } public String[] getBuildConfigurationTypes() { return buildConfigurationTypes; } /** * <p>Returns a working copy of an <code>IApplicationPermissions</code> * with default permissions.</p> * @param project * @return */ public IApplicationPermissions getDefaultPermissions(MoSyncProject project) { return ApplicationPermissions.getDefaultPermissions(project); } /** * Logs a specified method ONCE * @param e * @param token Used to distinguish the source of log messages */ public void logOnce(Exception e, String token) { Integer logCount = logCounts.get(token); if (logCount == null) { logCount = 0; } if (logCount < 1) { log(e); } logCount++; logCounts.put(token, logCount); } public ISecurePropertyOwner getSecureProperties() { return secureProperties; } public IProvider<PBEKeySpec, String> getPasswordProvider() { return passwordProvider; } public boolean usesEclipseSecureStorage() { return passwordProvider.usesEclipseSecureStorage(); } public void doUseEclipseSecureStorage(boolean useEclipseSecureStorage) throws CoreException { passwordProvider.doUseEclipseSecureStorage(useEclipseSecureStorage); } /** * Returns a 'workspace' token, that has a very large probability * of being unique per workspace. (It is randomly generated). * It is designed be used in filenames for workspace specific file lookup. * @return */ public String getWorkspaceToken() { return workspaceToken; } private void initWorkspaceToken() { if (getPreferenceStore().isDefault(WORKSPACE_TOKEN_PREF)) { byte[] random = new byte[6]; new Random(System.currentTimeMillis()).nextBytes(random); String workspaceToken = Util.toBase16(random); getPreferenceStore().setValue(WORKSPACE_TOKEN_PREF, workspaceToken); } this.workspaceToken = getPreferenceStore().getString(WORKSPACE_TOKEN_PREF); } /** * Generates a time-based 128-bit UUID. * @return */ public UUID generateUUID() { if (secureRnd == null) { secureRnd = new SecureRandom(); } // Generate a 48-bit random node id; set the multicast bit to 1 long nodeId = (secureRnd.nextLong() & 0xffffffffffffL) | 0x010000000000L; long clockSeq = secureRnd.nextInt(0x4fff); long clockSeq_lo = clockSeq & 0xff; long clockSeq_hi_and_res = ((clockSeq >> 16) & 0x4f) | 0x80; long lsb = (clockSeq_hi_and_res << 56) | (clockSeq_lo << 48) | nodeId; long utcTimestamp = System.currentTimeMillis(); long timestamp = DCE_OFFSET + 10000 * utcTimestamp; long timestamp_lo = timestamp & 0xffffffff; long timestamp_mid = (timestamp >> 32) & 0xffff; long timestamp_hi_and_version = ((timestamp >> 48) & 0x0fff) | 0x1000; long msb = (timestamp_lo << 32) | (timestamp_mid << 16) | timestamp_hi_and_version; return new UUID(msb, lsb); } /** * Returns the 'low memory manager' for notifications on low memory * @return */ public static synchronized LowMemoryManager getLowMemoryManager() { if (lowMemoryManager == null) { lowMemoryManager = new LowMemoryManager(0.85); } return lowMemoryManager; } public static ISavePolicy getSavePolicy() { if (savePolicy == null) { IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor(ISavePolicy.EXTENSION_POINT); try { savePolicy = ISavePolicy.NULL; if (!isHeadless() && elements.length == 1) { savePolicy = (ISavePolicy) elements[0].createExecutableExtension("implementation"); } } catch (Exception e) { // Ignore } } return savePolicy; } }