/* * Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved. * * This program and the accompanying materials are made available * under the terms of the Eclipse Public License, Version 1.0, * which accompanies this distribution and is available at * * http://www.eclipse.org/legal/epl-v10.html * */ package net.rim.ejde.internal.core; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.Hashtable; import java.util.MissingResourceException; import java.util.ResourceBundle; import net.rim.ejde.IDebugConsoleWriter; import net.rim.ejde.external.sourceMapper.SourceMapperAccess; import net.rim.ejde.internal.builders.ClasspathChangeManager; import net.rim.ejde.internal.builders.PreprocessedSourceMapper; import net.rim.ejde.internal.internalplugin.InternalFragment; import net.rim.ejde.internal.launching.EJDEDebugFilesClient; import net.rim.ejde.internal.launching.IFledgeLaunchConstants; import net.rim.ejde.internal.launching.IRunningFledgeLaunchConstants; import net.rim.ejde.internal.legacy.RIADialog; import net.rim.ejde.internal.model.BasicBlackBerryProperties; import net.rim.ejde.internal.model.BlackBerryProject; import net.rim.ejde.internal.model.BlackBerryProperties; import net.rim.ejde.internal.ui.consoles.SimulatorOutputConsole; import net.rim.ejde.internal.ui.preferences.PreferenceConstants; import net.rim.ejde.internal.util.DebugUtils; import net.rim.ejde.internal.util.InternalContextManagerUtils; import net.rim.ejde.internal.util.PackagingUtils; import net.rim.ejde.internal.util.RIAUtils; import net.rim.ejde.internal.util.UpgradingNotification; import net.rim.ejde.internal.util.VMToolsUtils; import net.rim.ejde.internal.validation.ValidationManager; import net.rim.ide.OSUtils; import net.rim.ide.RIA; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchesListener2; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.MessageConsoleStream; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import org.osgi.framework.Version; import com.thoughtworks.xstream.XStream; /** * The activator class controls the plug-in life cycle */ public class ContextManager extends AbstractUIPlugin implements IDebugEventSetListener, ILaunchesListener2 { static private final Logger log = Logger.getLogger( ContextManager.class ); private Hashtable< String, BlackBerryProperties > BBModelMap; private Hashtable< IPath, RIA > BBVMMap; private XStream _xStream; // The plug-in ID public static final String PLUGIN_ID = "net.rim.ejde"; // The shared instance public static ContextManager PLUGIN; private ResourceBundle _coreResourcesBundle; private boolean _isSuspended; private Image _blankImage; private HashMap< Object, Image > _images = new HashMap< Object, Image >(); { try { _coreResourcesBundle = ResourceBundle.getBundle( "CorePluginResources" ); //$NON-NLS-1$ } catch( MissingResourceException x ) { _coreResourcesBundle = null; } } /** * The constructor */ public ContextManager() { PLUGIN = this; } /* * (non-Javadoc) * * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext ) */ public void start( BundleContext context ) throws Exception { log.trace( "ContextManager starting, bundle version: " + context.getBundle().getVersion() ); super.start( context ); BBModelMap = new Hashtable< String, BlackBerryProperties >(); BBVMMap = new Hashtable< IPath, RIA >(); // register classpath change listener ClasspathChangeManager classpathChangeManager = ClasspathChangeManager.getInstance(); classpathChangeManager.addElementChangedListener(); // register property change listener PropertyChangeListenerImp.addListener(); BundleListenerHandler.getInstance( context ); RuntimeInstallsHandler.getInstance(); enableResourceChangeListener( true ); // set preprocess mapper SourceMapperAccess.setSourceMapper( new PreprocessedSourceMapper() ); // initialize validation manager ValidationManager.getInstance(); // Add launch and debug event listener used by debugging view DebugPlugin.getDefault().addDebugEventListener( this ); DebugPlugin.getDefault().getLaunchManager().addLaunchListener( this ); IPreferenceStore psc = ContextManager.getDefault().getPreferenceStore(); if( OSUtils.isMac() && !psc.getBoolean( IConstants.LC_FILTERING_SET_KEY ) ) { // For Mac, filter out simulator LaunchConfig(s), if not already // Once LC_filtering_set=true, there is no more forcing of this logic to allow diff testing IPreferenceStore psd = DebugUIPlugin.getDefault().getPreferenceStore(); if( !psd.getBoolean( IInternalDebugUIConstants.PREF_FILTER_LAUNCH_TYPES ) ) { psc.setValue( IConstants.LC_FILTERING_SET_KEY, true ); psd.setValue( IInternalDebugUIConstants.PREF_FILTER_LAUNCH_TYPES, true ); String pftl = psd.getString( IInternalDebugUIConstants.PREF_FILTER_TYPE_LIST ); if( pftl == null ) { pftl = ""; } if( !pftl.contains( IFledgeLaunchConstants.LAUNCH_CONFIG_ID ) ) { pftl += IFledgeLaunchConstants.LAUNCH_CONFIG_ID + IConstants.COMMA_MARK; } if( !pftl.contains( IRunningFledgeLaunchConstants.LAUNCH_CONFIG_ID ) ) { pftl += IRunningFledgeLaunchConstants.LAUNCH_CONFIG_ID + IConstants.COMMA_MARK; } psd.setValue( IInternalDebugUIConstants.PREF_FILTER_TYPE_LIST, pftl ); } } // check if we need to clean the workspace boolean needClean = psc.getBoolean( IConstants.NEED_CLEAN_WORKSPACE_KEY ); if( needClean ) { CleanWorkspaceJob job = new CleanWorkspaceJob( "Cleaning workspace" ); job.schedule(); } InternalContextManagerUtils.startupInitialize(); InternalFragment.startup(); } protected void refreshPluginActions() { super.refreshPluginActions(); boolean needUpdateCheck = ContextManager.getDefault().getPreferenceStore().getBoolean( PreferenceConstants.UPDATE_NOTIFY ); log.trace( "ContextManager startingneed update: " + needUpdateCheck ); if( needUpdateCheck ) { UpgradingNotification updateJob = new UpgradingNotification(); updateJob.schedule(); } } public XStream getXStream() { if( _xStream == null ) { _xStream = new XStream(); _xStream.alias( BasicBlackBerryProperties.MODEL_ALIAS_HEAD, BlackBerryProperties.class ); _xStream.alias( BasicBlackBerryProperties.PACKAGING__ALIAS_HEAD, BlackBerryProperties.ExtendedPackaging.class ); _xStream.processAnnotations( BlackBerryProperties.class ); } return _xStream; } /** * Get the RIA instance of the given <code>homePath</code>. * * @param homePath * @return */ public RIA getRIA( String homePath ) { return getRIA( new Path( homePath ) ); } /** * Get the RIA instance of the given <code>homePath<code>. * * @param homePath * the home path of the RIA * @return RIA instances or <code>null</code> if the corresponding RIA instance can not be found or created. */ public RIA getRIA( IPath homePath ) { if( homePath == null || homePath.isEmpty() ) { log.error( "getRIA(): RIA home path is empty." ); return null; } RIA ria = BBVMMap.get( homePath ); if( ria != null ) { return ria; } String legacyJDEHome = RIAUtils.getValidJDEHome( homePath.toOSString() ); if( StringUtils.isBlank( legacyJDEHome ) ) { log.error( "getRIA(): Invalid RIA home path: " + homePath.toOSString() ); return null; } // TODO:In the else part invoke the Mac/Linux version of executables if( OSUtils.isWindows() ) { RIAUtils.initDLLs(); } log.debug( "RIA Initialization is started for " + homePath + "....." ); long start = System.currentTimeMillis(); String DLLPath = ContextManager.PLUGIN.getStateLocation().append( "installDlls" ).toOSString(); long stop = System.currentTimeMillis(); ria = RiaMaker.createRia( legacyJDEHome, DLLPath ); // hook to RIA dialog callback for missing debug files ria.setDialogCallback( new RIADialog() ); // enable logging String enableLog = System.getProperties().getProperty( "JDWP_LOGGING" ); //$NON-NLS-1$ ria.setLogging( enableLog != null ); log.debug( "RIA Initialization finished in : " + ( stop - start ) + " ms" ); BBVMMap.put( homePath, ria ); IPreferenceStore store = ContextManager.getDefault().getPreferenceStore(); String serverURL = store.getString( PreferenceConstants.DEBUG_FILE_SERVER_URL ); try { IPath vmToolPath = VMToolsUtils.getVMToolsFolderPath(); ria.setDebugFilesClient( new EJDEDebugFilesClient( serverURL, vmToolPath.toString() + File.separator + "BundleInfo", new DebugConsoleWriter() ) ); } catch( IOException e ) { log.error( "Error retrieving vmtool path", e ); return null; } return ria; } /** * Set the given <code>ria</code> to the cached RIA map. * * @param ria */ synchronized public void setRIA( RIA ria ) { if( ria != null ) { BBVMMap.put( new Path( ria.getHomePath() ), ria ); } } /** * Enable ResourceChangeManager to listen to eclipse resource changes. */ public void enableResourceChangeListener( boolean enabled ) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); if( enabled ) { workspace.addResourceChangeListener( ResourceChangeManager.getInstance(), ResourceChangeManager.getFilter() ); } else { workspace.removeResourceChangeListener( ResourceChangeManager.getInstance() ); } } /* * (non-Javadoc) * * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext ) */ public void stop( BundleContext context ) throws Exception { super.stop( context ); BundleListenerHandler.removeInstance( context ); RuntimeInstallsHandler.removeInstance(); enableResourceChangeListener( false ); // remove property change listener PropertyChangeListenerImp.removeListener(); // remove debug event and launch listener DebugPlugin.getDefault().removeDebugEventListener( this ); DebugPlugin.getDefault().getLaunchManager().removeLaunchListener( this ); // stop RIAs RIA ria = null; for( IPath home : BBVMMap.keySet() ) { ria = BBVMMap.get( home ); if( ria != null ) { ria.stop( true ); } } BBModelMap = null; PLUGIN = null; InternalContextManagerUtils.stopCleanup(); } /** * Get the BlackBerry properties instances for the given <code>projectName</code>. * <p> * <b>This method needs to be called when a BlackBerry properties instance is required. </b> * * * @param projectName * @param forceLoad * <code>true</code> force to load the properties from the project description file; * <p> * <code>false</code> if the properties is in the cache, return the cached properties; * @return The BlackBerry properties of the project or <code>null</code> if the project is a BlackBerry project but does not * have a project description xml file or any exception occurred. */ public BlackBerryProperties getBBProperties( String projectName, boolean forceLoad ) { BlackBerryProperties properties = BBModelMap.get( projectName ); if( properties != null && !forceLoad ) { return properties; } IWorkspace eclipseWS = ResourcesPlugin.getWorkspace(); IWorkspaceRoot eclipseWSRoot = eclipseWS.getRoot(); IProject project = eclipseWSRoot.getProject( projectName ); // try to get properties from the project description file IFile propertiesFile = project.getFile( BlackBerryProject.METAFILE ); if( propertiesFile.exists() ) { properties = loadModelFromStore( propertiesFile.getLocation().toFile() ); if( properties != null ) { if( properties.getModelVersion().equals( "1.1.1" ) ) { boolean changed = false; // if the project property file was created by ejde 1.1.1, we change the output folder to the default value String oldOutputFolder = properties._packaging.getOutputFileName(); String defaultOutputFolder = PackagingUtils.getDefaultProjectOutputPrefix(); if( !oldOutputFolder.equalsIgnoreCase( defaultOutputFolder ) ) { log.trace( " Project property file was created by ejde 1.1.1, the output folder will be changed to the default value" ); properties._packaging.setOutputFolder( PackagingUtils.getDefaultProjectOutputPrefix() ); changed = true; } if( properties._application.getType().equals( BlackBerryProject.LIBRARY ) && !properties._application.isSystemModule() ) { log.trace( " Project property file was created by ejde 1.1.1, the isSystemModule value is changed to true for a lib project" ); properties._application.setIsSystemModule( true ); changed = true; } if( changed ) { properties.setModelVersion( BlackBerryProperties.getDefaultModelVersion() ); IStatus status = ResourcesPlugin.getWorkspace().validateEdit( new IFile[] { propertiesFile }, null ); if( status.isOK() ) { commitModelToStore( properties, propertiesFile ); } } } BBModelMap.put( projectName, properties ); } } else { properties = new BlackBerryProperties(); properties.setValidOutputFileName( project.getName() ); } return properties; } /** * Sets the given <code>properties</code> to the cached table. * <p> * <b>This method needs to be called when a BlackBerry properties instance is going to be set to the cached table or commit to * the filesystem. </b> * * @param name * name of the BlackBerryProject the properties belong to * @param properties * BlackBerryProperties * @param force * <code>true</code> if the given <code>properties</code> is already cached, we replace it with the given one and * commit it to file system * <p> * <code>false</code> if the given <code>properties</code> is already cached, do nothing * </p> */ public void setBBProperties( String name, BlackBerryProperties properties, boolean force ) { log.trace( "setBBProperties(); " + name + "; " + force ); BlackBerryProperties oldProperties = BBModelMap.get( name ); boolean isequ = properties.equals( oldProperties ); if( isequ && !force ) { return; } if( !isequ ) { BBModelMap.put( name, properties ); } IProject iProject = ResourcesPlugin.getWorkspace().getRoot().getProject( name ); IFile propertiesFile = iProject.getFile( BlackBerryProject.METAFILE ); try { if( propertiesFile.exists() ) { propertiesFile.deleteMarkers( null, false, IResource.DEPTH_ZERO ); } } catch( CoreException e ) { log.error( e ); } commitModelToStore( properties, propertiesFile ); } public void removeBBProperties( String projectName ) { BBModelMap.remove( projectName ); } /** * Returns the shared instance * * @return the shared instance */ public static ContextManager 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 ); } /** * Returns the plugin's resource bundle, */ public ResourceBundle getCoreResourcesBundle() { return _coreResourcesBundle; } /** * Returns the string from the plugin's resource bundle, or 'key' if not found. */ public static String getResourceString( String key ) { ResourceBundle bundle = PLUGIN.getCoreResourcesBundle(); try { return ( bundle != null ) ? bundle.getString( key ) : key; } catch( MissingResourceException e ) { return key; } } public static Shell getActiveWorkbenchShell() { IWorkbenchWindow window = getActiveWorkbenchWindow(); if( window != null ) { return window.getShell(); } return null; } public static IWorkbenchWindow getActiveWorkbenchWindow() { return PLUGIN.getWorkbench().getActiveWorkbenchWindow(); } /** * Gets the active workbench page. * * @return the active workbench page */ public static IWorkbenchPage getActiveWorkbenchPage() { IWorkbenchWindow window = getActiveWorkbenchWindow(); if( window == null ) { return null; } return window.getActivePage(); } public BlackBerryProperties loadModelFromStore( File file ) { BlackBerryProperties blackBerryProperties = null; if( null == file || !file.exists() || !file.isFile() ) { return null; } log.trace( "loading " + file.getPath() ); FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream( file ); if( 0 < fileInputStream.available() ) { XStream xStream = getXStream(); blackBerryProperties = (BlackBerryProperties) xStream.fromXML( fileInputStream ); } else { blackBerryProperties = new BlackBerryProperties(); } } catch( Throwable t ) { log.debug( "", t ); } finally { if( null != fileInputStream ) { try { fileInputStream.close(); } catch( IOException e ) { log.error( "Error closing input stream", e ); } } } log.trace( "finished loading " + file.getPath() ); return blackBerryProperties; } private void outputAppDescriptorHeaderComment( ByteArrayOutputStream outputStream ) throws IOException { StringBuffer temp = new StringBuffer( "<!-- " ); temp.append( "This file has been generated by the BlackBerry Plugin for Eclipse v" ); Version v = ResourcesPlugin.getPlugin().getBundle().getVersion(); temp.append( v.getMajor() ); temp.append( "." ); temp.append( v.getMinor() ); temp.append( "." ); temp.append( v.getMicro() ); temp.append( ". -->\n\n" ); outputStream.write( temp.toString().getBytes() ); } private void commitModelToStore( BlackBerryProperties blackBerryProperties, IFile ifile ) { if( null == blackBerryProperties ) { return; } if( null == ifile ) { return; } InputStream inputStream = null; ByteArrayOutputStream outputStream = null; try { log.trace( "Writing " + ifile.getLocation().toOSString() ); outputStream = new ByteArrayOutputStream(); XStream xStream = getXStream(); outputAppDescriptorHeaderComment( outputStream ); xStream.toXML( blackBerryProperties, outputStream ); inputStream = new ByteArrayInputStream( outputStream.toByteArray() ); if( ifile.exists() ) { ifile.setContents( inputStream, IFile.FORCE, new NullProgressMonitor() ); } else { ifile.create( inputStream, true, new NullProgressMonitor() ); } } catch( CoreException e ) { log.error( e.getMessage() ); } catch( Throwable t ) { log.error( t.getMessage() ); } finally { if( inputStream != null ) { try { inputStream.close(); } catch( IOException e ) { log.error( e.getMessage() ); } } if( outputStream != null ) { try { outputStream.close(); } catch( IOException e ) { log.error( e.getMessage() ); } } } log.trace( "Finish writing " + ifile.getLocation().toOSString() ); } static public Display getDisplay() { if( PlatformUI.isWorkbenchRunning() ) { return PlatformUI.getWorkbench().getDisplay(); } return Display.getDefault(); } /** * (no java doc) * * @see IDebugEventSetListener#handleDebugEvents(DebugEvent[]). * */ public void handleDebugEvents( DebugEvent[] events ) { if( events == null || events.length == 0 ) return; for( int i = 0; i < events.length; i++ ) { if ( !DebugUtils.isFromRIMLaunch( events[i] )){ continue; } if( events[ i ].getKind() == DebugEvent.SUSPEND && !_isSuspended ) { _isSuspended = true; } else if( events[ i ].getKind() == DebugEvent.RESUME && _isSuspended ) { _isSuspended = false; } } // end for } public boolean isSuspended() { return _isSuspended; } /** * @see ILaunchesListener2#launchesTerminated(ILaunch[]). */ public void launchesTerminated( ILaunch[] launches ) { if ( !DebugUtils.hasRIMLaunch(launches) ){ return; } RIA currentRIA = RIA.getCurrentDebugger(); if( currentRIA != null ) { // when debugger is terminated, close simulator as well currentRIA.stopSimulator(); } _isSuspended = false; } /** * @see ILaunchesListener#launchesRemoved(ILaunch[]). */ public void launchesRemoved( ILaunch[] launches ) { // do nothing } /** * @see ILaunchesListener#launchesAdded(ILaunch[]). */ public void launchesAdded( ILaunch[] launches ) { // do nothing } /** * @see ILaunchesListener#launchesChanged(ILaunch[]). */ public void launchesChanged( ILaunch[] launches ) { // do nothing } private class CleanWorkspaceJob extends WorkspaceJob { public CleanWorkspaceJob( String name ) { super( name ); // TODO Auto-generated constructor stub } public boolean belongsTo( Object family ) { return ResourcesPlugin.FAMILY_MANUAL_BUILD.equals( family ); } @Override public IStatus runInWorkspace( IProgressMonitor monitor ) throws CoreException { ResourcesPlugin.getWorkspace().build( IncrementalProjectBuilder.CLEAN_BUILD, monitor ); ContextManager.getDefault().getPreferenceStore().setValue( IConstants.NEED_CLEAN_WORKSPACE_KEY, false ); return Status.OK_STATUS; } } /** * Gets the install url. * * @return the install url */ public URL getInstallURL() { return getBundle().getEntry( "/" ); //$NON-NLS-1$ } /** * Gets the image from plugin. * * @param bundleID * the bundle id * @param path * the path * * @return the image from plugin */ public Image getImageFromPlugin( String bundleID, String path ) { ImageDescriptor desc = AbstractUIPlugin.imageDescriptorFromPlugin( bundleID, path ); return ( desc != null ) ? get( desc ) : getBlankImage(); } /** * Gets the blank image. * * @return the blank image */ public Image getBlankImage() { if( _blankImage == null ) { _blankImage = ImageDescriptor.getMissingImageDescriptor().createImage(); } return _blankImage; } /** * Gets the. * * @param desc * the desc * * @return the image */ public Image get( ImageDescriptor desc ) { Object key = desc; Image image = _images.get( key ); if( image == null ) { image = desc.createImage(); _images.put( key, image ); } return image; } private class DebugConsoleWriter implements IDebugConsoleWriter { private SimulatorOutputConsole _console; private MessageConsoleStream _stream; public DebugConsoleWriter() { _console = SimulatorOutputConsole.getInstance(); } public void init() { _stream = _console.newMessageStream(); } public void log(String message) { _stream.println( message ); } public void close() { try { if ( _stream != null) _stream.close(); } catch( IOException e ) { // do nothing } } } }