/* * 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.launching; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.Vector; import net.rim.ejde.internal.core.ContextManager; import net.rim.ejde.internal.core.IConstants; import net.rim.ejde.internal.model.BlackBerryProject; import net.rim.ejde.internal.model.BlackBerryProperties; import net.rim.ejde.internal.ui.launchers.LaunchUtils; import net.rim.ejde.internal.util.Messages; import net.rim.ejde.internal.util.PackagingUtils; import net.rim.ejde.internal.util.ProjectUtils; import net.rim.ejde.internal.util.StatusFactory; import net.rim.ejde.internal.util.VMToolsUtils; import net.rim.ejde.internal.util.VMUtils; import net.rim.ide.RIA; import net.rim.ide.core.Util; import org.apache.log4j.Logger; 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.Status; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.jdt.launching.IVMInstall; import org.eclipse.osgi.util.NLS; /** * Deploy cod files to simulator or device for debugging. * */ public class DeploymentTask { private static final Logger _log = Logger.getLogger( DeploymentTask.class ); private Set< BlackBerryProject > _projects; private ILaunchConfiguration _configuration; // if hot-swap on simulator or device is successful private static boolean _isHotswapSuccess; /** * Constructs a DeploymentTask instance. * * @param name * @param projects * @param configuration */ public DeploymentTask( String name, Set< BlackBerryProject > projects, ILaunchConfiguration configuration ) { _projects = projects; _configuration = configuration; } public IStatus run( IProgressMonitor monitor ) throws CoreException { IStatus ret = Status.OK_STATUS; LinkedHashSet< BlackBerryProject > projectSet = ProjectUtils.getProjectsByBuildOrder( _projects ); monitor.beginTask( "Deploying projects", projectSet.size() ); if( _configuration.getType().getIdentifier().equals( IDeviceLaunchConstants.LAUNCH_CONFIG_ID ) ) { ret = handleDeviceDeployment( monitor ); } else { ret = handleSimulatorDeployment( monitor ); } return ret; } private IStatus handleSimulatorDeployment( IProgressMonitor monitor ) throws CoreException { IStatus status = Status.OK_STATUS; ILaunch launch = LaunchUtils.getRunningBBLaunch(); if( launch != null ) { LaunchUtils.closeLaunch( launch ); status = deployProjectsToSDK( monitor ); if( status.isOK() ) { status = hotSwap( monitor ); } } else { status = deployProjectsToSDK( monitor ); } return status; } private IStatus handleDeviceDeployment( IProgressMonitor monitor ) throws CoreException { IStatus status = Status.OK_STATUS; ILaunch launch = LaunchUtils.getRunningBBLaunch(); if( launch != null ) { LaunchUtils.closeLaunch( launch ); } // Copy build artifacts to SDK folder first, this is needed by debugger to locate .debug files status = deployProjectsToSDK( monitor ); if( status.isOK() && _configuration.getAttribute( IDeviceLaunchConstants.DEPLOY_PROJECT_TO_DEVICE, true ) ) { _log.info( "Load projects on device" ); // Device deployment requires the latest JavaLoader that comes with CP5.0 or later. If JavaLoader does not support // device deployment, skip the step and go to attaching debugger. String javaLoaderVersion = VMToolsUtils.getStoredVersion(); if( javaLoaderVersion.compareTo( IConstants.SDK_FIVE_VERSION ) < 0 ) { _log.info( "Skip device deployment because Javaloader (" + javaLoaderVersion + ") does not support." ); return Status.OK_STATUS; } IVMInstall vm = LaunchUtils.getVMFromConfiguration( _configuration ); RIA debugger = ContextManager.PLUGIN.getRIA( vm.getInstallLocation().getAbsolutePath() ); if( debugger == null ) { return StatusFactory.createErrorStatus( NLS.bind( Messages.RIA_NO_RIA_INSTANCE_ERROR_MSG, vm.getName() ) ); } // get device PIN String debugDest = _configuration.getAttribute( IDeviceLaunchConstants.DEVICE, IDeviceLaunchConstants.ANY_DEVICE ); if( debugDest.equals( IDeviceLaunchConstants.ANY_DEVICE ) ) { String[] attachedDevices = debugger.getDebugDeviceList(); if( attachedDevices.length == 0 ) { return StatusFactory.createErrorStatus( Messages.DeviceLaunchConfigurationDelegate_noDeviceMsg ); } debugDest = attachedDevices[ 0 ]; } _log.debug( "Debug destination: " + debugDest ); String javaLoaderCmd = "javaloader"; IPath javaLoaderPath; try { javaLoaderPath = VMToolsUtils.getJavaLoaderPath(); // check the java loader again if( !VMToolsUtils.isVMToolValid() ) { return StatusFactory.createErrorStatus( Messages.JavaLoader_Not_Found_Msg ); } javaLoaderCmd = Util.quoteFile( javaLoaderPath.toOSString() ); } catch( IOException e ) { return StatusFactory.createErrorStatus( e.getMessage() ); } _log.debug( "set JavaLoader command: " + javaLoaderCmd ); debugger.setJavaLoaderCommand( javaLoaderCmd ); String[] javaLoaderOptions = debugger.getJavaLoaderOptions( debugDest ); // user enter a wrong password or cancel if( javaLoaderOptions == null ) { _log.trace( "Deployment Canceled!" ); return Status.CANCEL_STATUS; } // check if the device supports hot-swap if( debugger.deviceSupportsHotSwap( debugDest ) ) { status = deviceHotSwap( monitor, debugDest, _projects ); } else { String[] args = new String[javaLoaderOptions.length+1]; args[0] = javaLoaderCmd; for(int i = 0; i < javaLoaderOptions.length; i++ ){ args[i+1] = javaLoaderOptions[i]; } status = deviceNormalDeployment( monitor, args, _projects ); } } return status; } /** * Deploy the projects to SDK folder. * * @param monitor * @return * @throws CoreException */ private IStatus deployProjectsToSDK( IProgressMonitor monitor ) throws CoreException { IVMInstall vm = LaunchUtils.getVMFromConfiguration( _configuration ); DeviceInfo device = LaunchUtils.getDeviceToLaunch( _configuration ); String simDir = device.getDirectory(); String vmDir = LaunchUtils.getSimualtorPath( vm ); boolean internalMode = VMUtils.isInternal( vm ); for( BlackBerryProject bbProject : _projects ) { if( !ProjectUtils.hasCriticalProblems( bbProject.getProject() ) ) { // always deploy to VM folder first DeploymentHelper.deploy( bbProject, vmDir, internalMode ); if( !vmDir.equals( simDir ) ) { // this is external simulator, deploy to external simulator folder as well DeploymentHelper.deploy( bbProject, simDir, false ); } } if( monitor.isCanceled() ) { monitor.done(); return Status.CANCEL_STATUS; } monitor.worked( 1 ); } monitor.done(); return Status.OK_STATUS; } /** * Simulator hot-swap. * * @param monitor * @return * @throws CoreException */ private IStatus hotSwap( IProgressMonitor monitor ) throws CoreException { final RIA ria = RIA.getCurrentDebugger(); final List< File > files = getCodFiles( _projects ); if( files.isEmpty() ) { return StatusFactory.createErrorStatus( Messages.Luanch_Error_NoProjectToBeDeployed ); } ria.deployedFilesClear(); for( File file : files ) { ria.deployedFileAdd( file ); } Thread thread = new Thread() { public void run() { _log.debug( "Hot-swap: " + files ); _isHotswapSuccess = ria.deployFilesToSimulator(); } }; thread.start(); for( ;; ) { try { thread.join( 2000 ); } catch( InterruptedException e ) { // do nothing } if( monitor.isCanceled() ) { return Status.CANCEL_STATUS; } if( !thread.isAlive() ) { break; } } return _isHotswapSuccess ? Status.OK_STATUS : Status.CANCEL_STATUS; } /** * Device hot-swap. * * @param monitor * @param device * @param projects * @return * @throws CoreException */ private static IStatus deviceHotSwap( IProgressMonitor monitor, final String device, Collection< BlackBerryProject > projects ) throws CoreException { _log.debug( "Performing device hot-swap" ); final RIA ria = RIA.getCurrentDebugger(); final List< File > files = getJadFiles( projects ); if( files.isEmpty() ) { return StatusFactory.createErrorStatus( Messages.Luanch_Error_NoProjectToBeDeployed ); } ria.deployedFilesClear(); for( File file : files ) { ria.deployedFileAdd( file ); } Thread thread = new Thread() { public void run() { _log.debug( "Deploying files through RIA: " + files ); _isHotswapSuccess = ria.deployFilesToDevice( device ); _log.debug( _isHotswapSuccess ? "Deployment complete." : "Deployment failed" ); } }; thread.start(); for( ;; ) { try { thread.join( 2000 ); } catch( InterruptedException e ) { // do nothing } if( monitor.isCanceled() ) { return Status.CANCEL_STATUS; } if( !thread.isAlive() ) { break; } } return _isHotswapSuccess ? Status.OK_STATUS : Status.CANCEL_STATUS; } private static IStatus deviceNormalDeployment( IProgressMonitor monitor, String[] javaLoaderCmd, Collection< BlackBerryProject > projects ) { _log.debug( "Deploy projects on device, JavaLoader command:" + javaLoaderCmd ); List< File > deploymentFiles = getJadFiles( projects ); if( deploymentFiles.isEmpty() ) { return StatusFactory.createErrorStatus( Messages.Luanch_Error_NoProjectToBeDeployed ); } for( File f : deploymentFiles ) { String cmd = javaLoaderCmd.toString() + " load " + Util.quoteFile( f.getPath() ); _log.debug( "Deploying: " + cmd ); String[] args = new String[javaLoaderCmd.length+2]; for(int i = 0; i < javaLoaderCmd.length; i++ ){ args[i] = javaLoaderCmd[i]; } args[args.length-2] = "load"; args[args.length-1] = f.getPath(); Util.runCommand(args,new Vector(),null); } return Status.OK_STATUS; } private static List< File > getCodFiles( Collection< BlackBerryProject > projects ) { List< File > files = new ArrayList< File >(); for( BlackBerryProject project : projects ) { if( !ProjectUtils.hasCriticalProblems( project.getProject() ) ) { BlackBerryProperties properties = project.getProperties(); String outputFileName = properties._packaging.getOutputFileName(); String outputPath = project.getProject().getLocation().toOSString(); String[] outputPaths = PackagingUtils.getPackagingOutputFolders( project ); // we deploy the standard deliverables outputPath += IPath.SEPARATOR + outputPaths[ PackagingUtils.STANDARD_DEPLOYMENT ]; String deployFile = outputPath + File.separator + outputFileName + ".cod"; files.add( new File( deployFile ) ); } } return files; } private static List< File > getJadFiles( Collection< BlackBerryProject > projects ) { try { filterOutDependencyProjects( projects ); } catch( CoreException e ) { _log.error( e ); } List< File > files = new ArrayList< File >(); for( BlackBerryProject project : projects ) { if( !ProjectUtils.hasCriticalProblems( project.getProject() ) ) { BlackBerryProperties properties = project.getProperties(); String outputFileName = properties._packaging.getOutputFileName(); String outputPath = project.getProject().getLocation().toOSString(); String[] outputPaths = PackagingUtils.getPackagingOutputFolders( project ); // we deploy the standard deliverables outputPath += IPath.SEPARATOR + outputPaths[ PackagingUtils.STANDARD_DEPLOYMENT ]; // look for XXX_full.jad file first File deployFile = new File( outputPath + File.separator + outputFileName + IConstants.FULL_JAD_FILE_SUFFIX + IConstants.JAD_FILE_EXTENSION_WITH_DOT ); if( !deployFile.exists() ) { // if XXX.full.jad file does not exist, look for XXX.jad file deployFile = new File( outputPath + File.separator + outputFileName + IConstants.JAD_FILE_EXTENSION_WITH_DOT ); } files.add( deployFile ); } } return files; } /** * Filters out all dependency projects. We only want to deploy jad files of main projects. * * @param projects * @throws CoreException */ private static void filterOutDependencyProjects( Collection< BlackBerryProject > projects ) throws CoreException { Collection< BlackBerryProject > projectsNeedToBeRemoved = new HashSet< BlackBerryProject >(); List< BlackBerryProject > referencedProjects; BlackBerryProject bbProject; for( Iterator< BlackBerryProject > iterator = projects.iterator(); iterator.hasNext(); ) { bbProject = iterator.next(); referencedProjects = ProjectUtils.getAllReferencedProjects( bbProject ); for( BlackBerryProject referencedproject : referencedProjects ) { for( BlackBerryProject project : projects ) { if( referencedproject.getProject().getName().equals( project.getProject().getName() ) ) { projectsNeedToBeRemoved.add( project ); break; } } } } projects.removeAll( projectsNeedToBeRemoved ); } /** * Load the given {@link BlackBerryProject}s to device currently attached to USB port. * * @param projects * The projects * @param monitor * The progress monitor * @return The status * @throws CoreException */ public static IStatus loadProjectsToDevice( Collection< BlackBerryProject > projects, IProgressMonitor monitor ) throws CoreException { _log.debug( "Loading projects on device" ); // Close BlackBerry Launch if there is ILaunch launch = LaunchUtils.getRunningBBLaunch(); if( launch != null ) { LaunchUtils.closeLaunch( launch ); } monitor.beginTask( IConstants.EMPTY_STRING, 100 ); monitor.subTask( Messages.LoadProjectsOnDevice ); IVMInstall vm = ProjectUtils.getVMForProjects( projects ); RIA debugger = ContextManager.PLUGIN.getRIA( vm.getInstallLocation().getAbsolutePath() ); if( debugger == null ) { return StatusFactory.createErrorStatus( NLS.bind( Messages.RIA_NO_RIA_INSTANCE_ERROR_MSG, vm.getName() ) ); } // Get device PIN String[] attachedDevices = debugger.getDebugDeviceList(); if( attachedDevices.length == 0 ) { throw new CoreException( StatusFactory.createErrorStatus( Messages.DeviceLaunchConfigurationDelegate_noDeviceMsg ) ); } // Use the first device if multiple devices are found String debugDest = attachedDevices[ 0 ]; // Get JavaLoader command String javaLoaderCmd = "javaloader"; IPath javaLoaderPath; try { javaLoaderPath = VMToolsUtils.getJavaLoaderPath(); // check the java loader again if( !VMToolsUtils.isVMToolValid() ) { return StatusFactory.createErrorStatus( Messages.JavaLoader_Not_Found_Msg ); } javaLoaderCmd = Util.quoteFile( javaLoaderPath.toOSString() ); } catch( IOException e ) { return StatusFactory.createErrorStatus( e.getMessage() ); } monitor.worked( 20 ); _log.debug( "set JavaLoader command:" + javaLoaderCmd ); debugger.setJavaLoaderCommand( javaLoaderCmd ); // Get JavaLoader options _log.debug( "Get JavaLoader options" ); String[] javaLoaderOptions = debugger.getJavaLoaderOptions( debugDest ); _log.debug( "JavaLoader options:" + Arrays.toString(javaLoaderOptions) ); // user entered wrong password or canceled if( javaLoaderOptions == null ) { return Status.CANCEL_STATUS; } IStatus status; monitor.worked( 30 ); if( debugger.deviceSupportsHotSwap( debugDest ) ) { status = deviceHotSwap( monitor, debugDest, projects ); } else { String[] args = new String[javaLoaderOptions.length+1]; args[0] = javaLoaderCmd; for(int i = 0; i < javaLoaderOptions.length; i++ ){ args[i+1] = javaLoaderOptions[i]; } status = deviceNormalDeployment( monitor, args, projects ); } return status; } }