/*
* 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.model;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.jdt.internal.launching.LaunchingMessages;
import org.eclipse.jdt.internal.launching.LaunchingPlugin;
import org.eclipse.jdt.internal.launching.LibraryInfo;
import org.eclipse.jdt.launching.AbstractVMInstallType;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.LibraryLocation;
import org.eclipse.osgi.service.environment.Constants;
import com.ibm.icu.text.MessageFormat;
/**
* A VM install type for VMs the conform to the standard JDK installation layout.
*/
public class BlackBerryVMStandardInstallType extends AbstractVMInstallType {
public static final String ID_STANDARD_VM_TYPE = "org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType"; //$NON-NLS-1$
/**
* The root path for the attached source
*/
private String fDefaultRootPath = ""; //$NON-NLS-1$
/**
* Map of the install path for which we were unable to generate the library info during this session.
*/
private static Map fgFailedInstallPath = new HashMap();
/**
* Convenience handle to the system-specific file separator character
*/
private static final char fgSeparator = File.separatorChar;
/**
* The list of locations in which to look for the java executable in candidate VM install locations, relative to the VM
* install location.
*/
private static final String[] fgCandidateJavaFiles = {
"javaw", "javaw.exe", "java", "java.exe", "j9w", "j9w.exe", "j9", "j9.exe" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
private static final String[] fgCandidateJavaLocations = { "bin" + fgSeparator, "jre" + fgSeparator + "bin" + fgSeparator }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
/**
* Starting in the specified VM install location, attempt to find the 'java' executable file. If found, return the
* corresponding <code>File</code> object, otherwise return <code>null</code>.
*/
public static File findJavaExecutable( File vmInstallLocation ) {
// Try each candidate in order. The first one found wins. Thus, the order
// of fgCandidateJavaLocations and fgCandidateJavaFiles is significant.
for( int i = 0; i < fgCandidateJavaFiles.length; i++ ) {
for( int j = 0; j < fgCandidateJavaLocations.length; j++ ) {
File javaFile = new File( vmInstallLocation, fgCandidateJavaLocations[ j ] + fgCandidateJavaFiles[ i ] );
if( javaFile.isFile() ) {
return javaFile;
}
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.launching.IVMInstallType#getName()
*/
public String getName() {
return LaunchingMessages.StandardVMType_Standard_VM_3;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.launching.AbstractVMInstallType#doCreateVMInstall(java.lang.String)
*/
protected IVMInstall doCreateVMInstall( String id ) {
return new BlackBerrySDKInstall( this, id );
}
/**
* Return library information corresponding to the specified install location. If the information does not exist, create it
* using the given Java executable.
*/
protected synchronized LibraryInfo getLibraryInfo( File javaHome, File javaExecutable ) {
// See if we already know the information for the requested VM. If not, generate it.
String installPath = javaHome.getAbsolutePath();
LibraryInfo info = LaunchingPlugin.getLibraryInfo( installPath );
if( info == null ) {
info = (LibraryInfo) fgFailedInstallPath.get( installPath );
if( info == null ) {
info = generateLibraryInfo( javaHome, javaExecutable );
if( info == null ) {
info = getDefaultLibraryInfo( javaHome );
fgFailedInstallPath.put( installPath, info );
} else {
// only persist if we were able to generate information - see bug 70011
LaunchingPlugin.setLibraryInfo( installPath, info );
}
}
}
return info;
}
/**
* Return <code>true</code> if the appropriate system libraries can be found for the specified java executable,
* <code>false</code> otherwise.
*/
protected boolean canDetectDefaultSystemLibraries( File javaHome, File javaExecutable ) {
LibraryLocation[] locations = getDefaultLibraryLocations( javaHome );
String version = getVMVersion( javaHome, javaExecutable );
return locations.length > 0 && !version.startsWith( "1.1" ); //$NON-NLS-1$
}
/**
* Returns the version of the VM at the given location, with the given executable.
*
* @param javaHome
* @param javaExecutable
* @return String
*/
protected String getVMVersion( File javaHome, File javaExecutable ) {
LibraryInfo info = getLibraryInfo( javaHome, javaExecutable );
return info.getVersion();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.launching.IVMInstallType#detectInstallLocation()
*/
public File detectInstallLocation() {
// do not detect on the Mac OS
if( Platform.getOS().equals( Constants.OS_MACOSX ) ) {
return null;
}
// Retrieve the 'java.home' system property. If that directory doesn't exist,
// return null.
File javaHome;
try {
javaHome = new File( System.getProperty( "java.home" ) ).getCanonicalFile(); //$NON-NLS-1$
} catch( IOException e ) {
LaunchingPlugin.log( e );
return null;
}
if( !javaHome.exists() ) {
return null;
}
// Find the 'java' executable file under the java home directory. If it can't be
// found, return null.
File javaExecutable = findJavaExecutable( javaHome );
if( javaExecutable == null ) {
return null;
}
// If the reported java home directory terminates with 'jre', first see if
// the parent directory contains the required libraries
boolean foundLibraries = false;
if( javaHome.getName().equalsIgnoreCase( "jre" ) ) { //$NON-NLS-1$
File parent = new File( javaHome.getParent() );
if( canDetectDefaultSystemLibraries( parent, javaExecutable ) ) {
javaHome = parent;
foundLibraries = true;
}
}
// If we haven't already found the libraries, look in the reported java home dir
if( !foundLibraries ) {
if( !canDetectDefaultSystemLibraries( javaHome, javaExecutable ) ) {
return null;
}
}
return javaHome;
}
/**
* Return an <code>IPath</code> corresponding to the single library file containing the standard Java classes for most VMs
* version 1.2 and above.
*/
protected IPath getDefaultSystemLibrary( File javaHome ) {
IPath jreLibPath = new Path( javaHome.getPath() ).append( "lib" ).append( "rt.jar" ); //$NON-NLS-2$ //$NON-NLS-1$
if( jreLibPath.toFile().isFile() ) {
return jreLibPath;
}
return new Path( javaHome.getPath() ).append( "jre" ).append( "lib" ).append( "rt.jar" ); //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
}
/**
* Returns a path to the source attachment for the given library, or an empty path if none.
*
* @param libLocation
* @return a path to the source attachment for the given library, or an empty path if none
*/
protected IPath getDefaultSystemLibrarySource( File libLocation ) {
File parent = libLocation.getParentFile();
while( parent != null ) {
File parentsrc = new File( parent, "src.jar" ); //$NON-NLS-1$
if( parentsrc.isFile() ) {
setDefaultRootPath( "src" );//$NON-NLS-1$
return new Path( parentsrc.getPath() );
}
parentsrc = new File( parent, "src.zip" ); //$NON-NLS-1$
if( parentsrc.isFile() ) {
setDefaultRootPath( "" ); //$NON-NLS-1$
return new Path( parentsrc.getPath() );
}
parent = parent.getParentFile();
}
// if we didn't find any of the normal source files, look for J9 source
IPath result = checkForJ9LibrarySource( libLocation );
if( result != null )
return result;
setDefaultRootPath( "" ); //$NON-NLS-1$
return Path.EMPTY;
}
// J9 has a known/fixed structure for its libs and source locations. Here just
// look for the source associated with each lib.
private IPath checkForJ9LibrarySource( File libLocation ) {
File parent = libLocation.getParentFile();
String name = libLocation.getName();
if( name.equalsIgnoreCase( "classes.zip" ) ) { //$NON-NLS-1$
File source = new File( parent, "source/source.zip" ); //$NON-NLS-1$
return source.isFile() ? new Path( source.getPath() ) : Path.EMPTY;
}
if( name.equalsIgnoreCase( "locale.zip" ) ) { //$NON-NLS-1$
File source = new File( parent, "source/locale-src.zip" ); //$NON-NLS-1$
return source.isFile() ? new Path( source.getPath() ) : Path.EMPTY;
}
if( name.equalsIgnoreCase( "charconv.zip" ) ) { //$NON-NLS-1$
File source = new File( parent, "charconv-src.zip" ); //$NON-NLS-1$
return source.isFile() ? new Path( source.getPath() ) : Path.EMPTY;
}
return null;
}
protected IPath getDefaultPackageRootPath() {
return new Path( getDefaultRootPath() );
}
/**
* NOTE: We do not add libraries from the "endorsed" directory explicitly, as the bootpath contains these entries already (if
* they exist).
*
* @see org.eclipse.jdt.launching.IVMInstallType#getDefaultLibraryLocations(File)
*/
public LibraryLocation[] getDefaultLibraryLocations( File installLocation ) {
// Determine the java executable that corresponds to the specified install location
// and use this to generate library information. If no java executable was found,
// the 'standard' libraries will be returned.
File javaExecutable = findJavaExecutable( installLocation );
LibraryInfo libInfo;
if( javaExecutable == null ) {
libInfo = getDefaultLibraryInfo( installLocation );
} else {
libInfo = getLibraryInfo( installLocation, javaExecutable );
}
// Add all endorsed libraries - they are first, as they replace
List allLibs = new ArrayList( gatherAllLibraries( libInfo.getEndorsedDirs() ) );
// next is the boot path libraries
String[] bootpath = libInfo.getBootpath();
List boot = new ArrayList( bootpath.length );
URL url = getDefaultJavadocLocation( installLocation );
for( int i = 0; i < bootpath.length; i++ ) {
IPath path = new Path( bootpath[ i ] );
File lib = path.toFile();
if( lib.exists() && lib.isFile() ) {
LibraryLocation libraryLocation = new LibraryLocation( path, getDefaultSystemLibrarySource( lib ),
getDefaultPackageRootPath(), url );
boot.add( libraryLocation );
}
}
allLibs.addAll( boot );
// Add all extension libraries
allLibs.addAll( gatherAllLibraries( libInfo.getExtensionDirs() ) );
// remove duplicates
HashSet set = new HashSet();
LibraryLocation lib = null;
for( ListIterator liter = allLibs.listIterator(); liter.hasNext(); ) {
lib = (LibraryLocation) liter.next();
IPath systemLibraryPath = lib.getSystemLibraryPath();
String device = systemLibraryPath.getDevice();
if( device != null ) {
// @see Bug 197866 - Installed JRE Wizard creates duplicate system libraries when drive letter is lower case
systemLibraryPath = systemLibraryPath.setDevice( device.toUpperCase() );
}
if( !set.add( systemLibraryPath.toOSString() ) ) {
// did not add it, duplicate
liter.remove();
}
}
return (LibraryLocation[]) allLibs.toArray( new LibraryLocation[ allLibs.size() ] );
}
/**
* Returns default library information for the given install location.
*
* @param installLocation
* @return LibraryInfo
*/
protected LibraryInfo getDefaultLibraryInfo( File installLocation ) {
IPath rtjar = getDefaultSystemLibrary( installLocation );
File extDir = getDefaultExtensionDirectory( installLocation );
File endDir = getDefaultEndorsedDirectory( installLocation );
String[] dirs = null;
if( extDir == null ) {
dirs = new String[ 0 ];
} else {
dirs = new String[] { extDir.getAbsolutePath() };
}
String[] endDirs = null;
if( endDir == null ) {
endDirs = new String[ 0 ];
} else {
endDirs = new String[] { endDir.getAbsolutePath() };
}
return new LibraryInfo( "???", new String[] { rtjar.toOSString() }, dirs, endDirs ); //$NON-NLS-1$
}
/**
* Returns a list of all zip's and jars contained in the given directories.
*
* @param dirPaths
* a list of absolute paths of directories to search
* @return List of all zip's and jars
*/
public static List gatherAllLibraries( String[] dirPaths ) {
List libraries = new ArrayList();
for( int i = 0; i < dirPaths.length; i++ ) {
File extDir = new File( dirPaths[ i ] );
if( extDir.exists() && extDir.isDirectory() ) {
String[] names = extDir.list();
if( names != null ) {
for( int j = 0; j < names.length; j++ ) {
String name = names[ j ];
File jar = new File( extDir, name );
if( jar.isFile() ) {
int length = name.length();
if( length > 4 ) {
String suffix = name.substring( length - 4 );
if( suffix.equalsIgnoreCase( ".zip" ) || suffix.equalsIgnoreCase( ".jar" ) ) { //$NON-NLS-1$ //$NON-NLS-2$
try {
IPath libPath = new Path( jar.getCanonicalPath() );
LibraryLocation library = new LibraryLocation( libPath, Path.EMPTY, Path.EMPTY, null );
libraries.add( library );
} catch( IOException e ) {
LaunchingPlugin.log( e );
}
}
}
}
}
}
}
}
return libraries;
}
/**
* Returns the default location of the extension directory, based on the given install location. The resulting file may not
* exist, or be <code>null</code> if an extension directory is not supported.
*
* @param installLocation
* @return default extension directory or <code>null</code>
*/
protected File getDefaultExtensionDirectory( File installLocation ) {
File jre = null;
if( installLocation.getName().equalsIgnoreCase( "jre" ) ) { //$NON-NLS-1$
jre = installLocation;
} else {
jre = new File( installLocation, "jre" ); //$NON-NLS-1$
}
File lib = new File( jre, "lib" ); //$NON-NLS-1$
File ext = new File( lib, "ext" ); //$NON-NLS-1$
return ext;
}
/**
* Returns the default location of the endorsed directory, based on the given install location. The resulting file may not
* exist, or be <code>null</code> if an endorsed directory is not supported.
*
* @param installLocation
* @return default endorsed directory or <code>null</code>
*/
protected File getDefaultEndorsedDirectory( File installLocation ) {
File lib = new File( installLocation, "lib" ); //$NON-NLS-1$
File ext = new File( lib, "endorsed" ); //$NON-NLS-1$
return ext;
}
protected String getDefaultRootPath() {
return fDefaultRootPath;
}
protected void setDefaultRootPath( String defaultRootPath ) {
fDefaultRootPath = defaultRootPath;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.launching.IVMInstallType#validateInstallLocation(java.io.File)
*/
public IStatus validateInstallLocation( File javaHome ) {
IStatus status = null;
if( Platform.getOS().equals( Constants.OS_MACOSX ) ) {
status = new Status( IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0,
LaunchingMessages.StandardVMType_Standard_VM_not_supported_on_MacOS__1, null );
} else {
File javaExecutable = findJavaExecutable( javaHome );
if( javaExecutable == null ) {
status = new Status( IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0,
LaunchingMessages.StandardVMType_Not_a_JDK_Root__Java_executable_was_not_found_1, null ); //
} else {
if( canDetectDefaultSystemLibraries( javaHome, javaExecutable ) ) {
status = new Status( IStatus.OK, LaunchingPlugin.getUniqueIdentifier(), 0,
LaunchingMessages.StandardVMType_ok_2, null );
} else {
status = new Status( IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), 0,
LaunchingMessages.StandardVMType_Not_a_JDK_root__System_library_was_not_found__1, null );
}
}
}
return status;
}
/**
* Generates library information for the given java executable. A main program is run (
* <code>org.eclipse.jdt.internal.launching.support.
* LibraryDetector</code>), that dumps the system properties for bootpath and extension directories. This output is then
* parsed and cached for future reference.
*
* @return library info or <code>null</code> if none
*/
protected LibraryInfo generateLibraryInfo( File javaHome, File javaExecutable ) {
LibraryInfo info = null;
// if this is 1.1.X, the properties will not exist
IPath classesZip = new Path( javaHome.getAbsolutePath() ).append( "lib" ).append( "classes.zip" ); //$NON-NLS-1$ //$NON-NLS-2$
if( classesZip.toFile().exists() ) {
return new LibraryInfo( "1.1.x", new String[] { classesZip.toOSString() }, new String[ 0 ], new String[ 0 ] ); //$NON-NLS-1$
}
// locate the launching support jar - it contains the main program to run
File file = LaunchingPlugin.getFileInPlugin( new Path( "lib/launchingsupport.jar" ) ); //$NON-NLS-1$
if( file.exists() ) {
String javaExecutablePath = javaExecutable.getAbsolutePath();
String[] cmdLine = new String[] { javaExecutablePath,
"-classpath", file.getAbsolutePath(), "org.eclipse.jdt.internal.launching.support.LibraryDetector" }; //$NON-NLS-1$ //$NON-NLS-2$
Process p = null;
try {
String envp[] = null;
if( Platform.OS_MACOSX.equals( Platform.getOS() ) ) {
Map map = DebugPlugin.getDefault().getLaunchManager().getNativeEnvironmentCasePreserved();
if( map.remove( BlackBerryDebuggerRunner.JAVA_JVM_VERSION ) != null ) {
envp = new String[ map.size() ];
Iterator iterator = map.entrySet().iterator();
int i = 0;
while( iterator.hasNext() ) {
Entry entry = (Entry) iterator.next();
envp[ i ] = (String) entry.getKey() + "=" + (String) entry.getValue(); //$NON-NLS-1$
i++;
}
}
}
p = DebugPlugin.exec( cmdLine, null, envp );
IProcess process = DebugPlugin.newProcess( new Launch( null, ILaunchManager.RUN_MODE, null ), p,
"Library Detection" ); //$NON-NLS-1$
for( int i = 0; i < 600; i++ ) {
// Wait no more than 30 seconds (600 * 50 mils)
if( process.isTerminated() ) {
break;
}
try {
Thread.sleep( 50 );
} catch( InterruptedException e ) {
}
}
info = parseLibraryInfo( process );
} catch( CoreException ioe ) {
LaunchingPlugin.log( ioe );
} finally {
if( p != null ) {
p.destroy();
}
}
}
if( info == null ) {
// log error that we were unable to generate library information - see bug 70011
LaunchingPlugin.log( MessageFormat.format(
"Failed to retrieve default libraries for {0}", new String[] { javaHome.getAbsolutePath() } ) ); //$NON-NLS-1$
}
return info;
}
/**
* Parses the output from 'LibraryDetector'.
*/
protected LibraryInfo parseLibraryInfo( IProcess process ) {
IStreamsProxy streamsProxy = process.getStreamsProxy();
String text = null;
if( streamsProxy != null ) {
text = streamsProxy.getOutputStreamMonitor().getContents();
}
if( text != null && text.length() > 0 ) {
int index = text.indexOf( "|" ); //$NON-NLS-1$
if( index > 0 ) {
String version = text.substring( 0, index );
text = text.substring( index + 1 );
index = text.indexOf( "|" ); //$NON-NLS-1$
if( index > 0 ) {
String bootPaths = text.substring( 0, index );
String[] bootPath = parsePaths( bootPaths );
text = text.substring( index + 1 );
index = text.indexOf( "|" ); //$NON-NLS-1$
if( index > 0 ) {
String extDirPaths = text.substring( 0, index );
String endorsedDirsPath = text.substring( index + 1 );
String[] extDirs = parsePaths( extDirPaths );
String[] endDirs = parsePaths( endorsedDirsPath );
return new LibraryInfo( version, bootPath, extDirs, endDirs );
}
}
}
}
return null;
}
protected String[] parsePaths( String paths ) {
List list = new ArrayList();
int pos = 0;
int index = paths.indexOf( File.pathSeparatorChar, pos );
while( index > 0 ) {
String path = paths.substring( pos, index );
list.add( path );
pos = index + 1;
index = paths.indexOf( File.pathSeparatorChar, pos );
}
String path = paths.substring( pos );
if( !path.equals( "null" ) ) { //$NON-NLS-1$
list.add( path );
}
return (String[]) list.toArray( new String[ list.size() ] );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.launching.IVMInstallType#disposeVMInstall(java.lang.String)
*/
public void disposeVMInstall( String id ) {
IVMInstall vm = findVMInstall( id );
if( vm != null ) {
String path = vm.getInstallLocation().getAbsolutePath();
LaunchingPlugin.setLibraryInfo( path, null );
fgFailedInstallPath.remove( path );
}
super.disposeVMInstall( id );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.launching.AbstractVMInstallType#getDefaultJavadocLocation(java.io.File)
*/
public URL getDefaultJavadocLocation( File installLocation ) {
File javaExecutable = findJavaExecutable( installLocation );
if( javaExecutable != null ) {
LibraryInfo libInfo = getLibraryInfo( installLocation, javaExecutable );
if( libInfo != null ) {
String version = libInfo.getVersion();
return getDefaultJavadocLocation( version );
}
}
return null;
}
/**
* Returns a default Javadoc location for a language version, or <code>null</code>.
*
* @param version
* language version such as "1.4"
* @return URL to default Javadoc location, or <code>null</code>
*/
public static URL getDefaultJavadocLocation( String version ) {
try {
if( version.startsWith( "1.6" ) ) { //$NON-NLS-1$
return new URL( "http://java.sun.com/javase/6/docs/api/" ); //$NON-NLS-1$
} else if( version.startsWith( "1.5" ) ) { //$NON-NLS-1$
return new URL( "http://java.sun.com/j2se/1.5.0/docs/api/" ); //$NON-NLS-1$
} else if( version.startsWith( "1.4" ) ) { //$NON-NLS-1$
return new URL( "http://java.sun.com/j2se/1.4.2/docs/api/" ); //$NON-NLS-1$
} else if( version.startsWith( "1.3" ) ) { //$NON-NLS-1$
return new URL( "http://java.sun.com/j2se/1.3/docs/api/" ); //$NON-NLS-1$
} else if( version.startsWith( "1.2" ) ) { //$NON-NLS-1$
return new URL( "http://java.sun.com/products/jdk/1.2/docs/api" ); //$NON-NLS-1$
}
} catch( MalformedURLException e ) {
}
return null;
}
}