package org.opensourcephysics.tools;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.JOptionPane;
import javax.swing.Timer;
import org.opensourcephysics.controls.OSPLog;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.display.OSPRuntime;
/**
* ExtensionsManager manages Java extensions directories.
* Its primary use is to copy Xuggle jars and QTJava.zip into appropriate ext directories.
*
* @author Douglas Brown
* @version 1.0
*/
public class ExtensionsManager {
private static final ExtensionsManager MANAGER = new ExtensionsManager();
private static boolean isReady = false, isSearching = false;
private static Set<File> allJavaExtensionDirectories = new TreeSet<File>();
private static int vmsFound = 0;
private static Timer timer;
String xuggleHome;
ExtensionsFilter extFilter;
/**
* Main method when used as a stand-alone application.
* @param args ignored
*/
public static void main(String[] args) {
// set up timer to allow the user to cancel searching every 30 seconds
timer = new Timer(30000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
int selected = JOptionPane.showConfirmDialog(null,
ToolsRes.getString("ExtensionsManager.Dialog.SlowSearch.Message1")+"\n"+ //$NON-NLS-1$ //$NON-NLS-2$
ToolsRes.getString("ExtensionsManager.Dialog.SlowSearch.Message2")+" "+vmsFound+".\n"+ //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
ToolsRes.getString("ExtensionsManager.Dialog.SlowSearch.Message3"), //$NON-NLS-1$
ToolsRes.getString("ExtensionsManager.Dialog.SlowSearch.Title"), //$NON-NLS-1$
JOptionPane.YES_NO_OPTION);
if(selected==JOptionPane.NO_OPTION) {
// print whatever has been found
synchronized(allJavaExtensionDirectories) {
printExtensionDirectoriesForBitrock(allJavaExtensionDirectories);
}
System.exit(0);
}
else timer.restart();
}
});
timer.setRepeats(true);
timer.start();
// File qtJava = getManager().getQTJavaZip();
// System.out.println(qtJava);
System.exit(0);
// // print list of extensions to stdout for use by Bitrock installers
// Set<File> extDirs = getManager().findJavaExtensionDirectories();
// printExtensionDirectoriesForBitrock(extDirs);
// System.exit(0);
}
/**
* Gets the singleton ExtensionManager.
* @return the ExtensionManager
*/
public static ExtensionsManager getManager() {
return MANAGER;
}
public static boolean isReady() {
return isReady;
}
/**
* Private constructor.
*/
private ExtensionsManager() {
xuggleHome = System.getenv("XUGGLE_HOME"); //$NON-NLS-1$
extFilter = new ExtensionsFilter();
}
/**
* Finds extension directories and prints a space-delimited list to System.out.
* A single space delimiter is parsable by Bitrock installers.
*/
private static void printExtensionDirectoriesForBitrock(Set<File> extDirs) {
String separator = " "; //$NON-NLS-1$
StringBuffer buf = new StringBuffer(2);
for (File next: extDirs) {
String fileName = XML.forwardSlash(next.getPath());
buf.append("\""+fileName+"\""+separator); //$NON-NLS-1$ //$NON-NLS-2$
}
String s = buf.toString();
// remove last separator
if (s.length()>=separator.length()) {
s = s.substring(0, s.length()-separator.length());
}
System.out.print(s);
}
/**
* Copies Xuggle jar files to a target directory. Does nothing if the directory
* already contains a xuggle-xuggler.jar of the same size.
*
* @param dir the directory
* @return true if jars are copied
*/
public boolean copyXuggleJarsTo(File dir) {
if (xuggleHome==null) {
xuggleHome = (String)OSPRuntime.getPreference("XUGGLE_HOME"); //$NON-NLS-1$
}
if (xuggleHome==null || dir==null) {
return false;
}
if (!new File(xuggleHome+"/share/java/jars/xuggle-xuggler.jar").exists()) { //$NON-NLS-1$
return false;
}
File xuggleJarDir = new File(xuggleHome+"/share/java/jars"); //$NON-NLS-1$
String[] jarNames = DiagnosticsForXuggle.xuggleJarNames;
File xuggleFile = new File(xuggleJarDir, jarNames[0]);
long fileLength = xuggleFile.length();
File extFile = new File(dir, jarNames[0]);
// copy xuggle jars
if (!extFile.exists() || extFile.length()!=fileLength) {
for (String next: jarNames) {
xuggleFile = new File(xuggleJarDir, next);
extFile = new File(dir, next);
if (!copyFile(xuggleFile, extFile)) {
return false;
}
}
// all jars were copied
return true;
}
return false;
}
/**
* Gets the xuggle-xuggler.jar from the xuggleHome directory, if it exists.
*
* @return xuggle-xuggler.jar
*/
public File getXuggleJar() {
if (xuggleHome==null) {
return null;
}
File xuggleJar = new File(xuggleHome+"/share/java/jars/xuggle-xuggler.jar"); //$NON-NLS-1$
if (xuggleJar.exists()) {
return xuggleJar;
}
return null;
}
/**
* Copies QTJava.zip to a target directory. Does not overwrite a later version.
*
* @param dir the directory
* @return true if copied
*/
public boolean copyQTJavaTo(File dir) {
File qtSource = getQTJavaZip(); // file to be copied
if (qtSource==null) return false;
File extFile = new File(dir, qtSource.getName());
long modified = qtSource.lastModified();
// copy qtSource to directory if newer
if (!extFile.exists() || extFile.lastModified()<modified) {
if (!copyFile(qtSource, extFile)) {
return false;
}
return true;
}
return false;
}
/**
* Returns the QTJava.zip file with the latest modified date.
*
* @return the QTJava.zip file, or null if none found
*/
public File getQTJavaZip() {
String qtJarName = "QTJava.zip"; //$NON-NLS-1$
String currentDir = OSPRuntime.getLaunchJarPath();
if (currentDir!=null) {
currentDir = XML.getDirectoryPath(currentDir);
}
else {
currentDir = "."; //$NON-NLS-1$
}
String[] folderNames = {
currentDir,
"C:/Program Files/QuickTime/QTSystem", //$NON-NLS-1$
"C:/Program Files (x86)/QuickTime/QTSystem", //$NON-NLS-1$
"C:/windows/system32", //$NON-NLS-1$
"C:/windows/system", //$NON-NLS-1$
"C:/winNT/system32", //$NON-NLS-1$
"system/library/java/extensions"}; //$NON-NLS-1$
// look for most recent QTJava.zip in system folders
long modified = 0;
File qtSource = null; // file to be returned
for (String next: folderNames) {
File qtFile = new File(next, qtJarName);
if (!qtFile.exists()) continue;
long date = qtFile.lastModified();
if (date>modified) {
modified = date;
qtSource = qtFile;
}
}
return qtSource;
}
/**
* Finds all java extension directories on the current machine.
* Win: typical jdk: Program Files\Java\jdkX.X.X_XX\jre\bin\java.exe
* typical jre: Program Files\Java\jreX.X.X_XX\bin\java.exe
* or Program Files\Java\jreX\bin\java.exe
* typical 32-bit jdk in 64-bit Windows: Program Files(x86)\Java\jdkX.X.X_XX\jre\bin\java.exe
* OSX: typical: /System/Library/Java/JavaVirtualMachines/X.X.X.jdk/Contents/Home/bin/java
* symlink at: /Library/Java/Home/bin/java
* also in /Library/Java/JavaVirtualMachines?
* Linux: typical: /usr/lib/jvm/java-X-openjdk/jre/bin/java
* symlink at: /usr/lib/jvm/java-X.X.X-openjdk/jre/bin/java
* sun versions: java-X-sun and java-X.X.X-sun
*
* @return a collection of java extension directory files
*/
private Set<File> findAllJavaExtensionDirectories() {
while (isSearching) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
if (!isReady) {
isSearching = true;
vmsFound = 0;
// set of extension directories used by the running Java VM
Set<String> vmExtDirs = new TreeSet<String>();
// set of "Java level" directories to search
Set<File> searchPaths = new TreeSet<File>();
try {
// get and parse system extension directories property into vmExtDirs
String paths = XML.forwardSlash(System.getProperty("java.ext.dirs")); //$NON-NLS-1$
String separator = System.getProperty("path.separator"); //$NON-NLS-1$
int n = paths.indexOf(separator);
while (n>-1) {
vmExtDirs.add(paths.substring(0, n));
paths = paths.substring(n+1);
n = paths.indexOf(separator);
}
if (!"".equals(paths)) {//$NON-NLS-1$
vmExtDirs.add(paths);
}
for (String next: vmExtDirs) {
File dir = new File(next);
if (!dir.exists()) continue;
synchronized(allJavaExtensionDirectories) {
allJavaExtensionDirectories.add(dir);
vmsFound++;
}
if (OSPRuntime.isMac()) {
// search path: /JavaVirtualMachines
while (dir!=null && dir.getPath().indexOf("/JavaVirtualMachines")>-1) { //$NON-NLS-1$
if (dir.getName().equals("JavaVirtualMachines")) { //$NON-NLS-1$
searchPaths.add(dir);
break;
}
dir = dir.getParentFile();
}
}
else if (OSPRuntime.isLinux()) {
// search path: /jvm
while (dir!=null && dir.getPath().indexOf("/jvm")>-1) { //$NON-NLS-1$
if (dir.getName().equals("jvm")) { //$NON-NLS-1$
searchPaths.add(dir);
break;
}
dir = dir.getParentFile();
}
}
}
if (OSPRuntime.isWindows()) {
String progfiles = System.getenv("ProgramFiles"); //$NON-NLS-1$
String w6432 = System.getenv("ProgramW6432"); //$NON-NLS-1$
String x86 = System.getenv("ProgramFiles(x86)"); //$NON-NLS-1$
// add Program Files (may or may not be x86) Java directory to search path if it exists
if (progfiles!=null) {
File file = new File(progfiles, "Java"); //$NON-NLS-1$
if (file.exists()) searchPaths.add(file);
}
// add "Program Files" Java directory to search path if it exists
if (w6432!=null) { // 64-bit Windows
File file = new File(w6432, "Java"); //$NON-NLS-1$
if (file.exists()) searchPaths.add(file);
}
// add "Program Files (x86)" Java directory to search path if it exists
if (x86!=null) { // 64-bit Windows
// add x86 Java directory to search path if it exists
File file = new File(x86, "Java"); //$NON-NLS-1$
if (file.exists()) searchPaths.add(file);
}
}
// search all searchPaths and add all extensions directories found
for (File next: searchPaths) {
findJavaExtensionDirectories(next, allJavaExtensionDirectories);
}
} catch (Exception e) {
}
isReady = true;
isSearching = false;
// log results for trouble-shooting
StringBuffer buf =new StringBuffer("Java extension directories: "); //$NON-NLS-1$
for (File next: allJavaExtensionDirectories) {
buf.append(next.getAbsolutePath()+", "); //$NON-NLS-1$
}
OSPLog.fine(buf.toString());
}
return new TreeSet<File>(allJavaExtensionDirectories);
}
/**
* Finds java extension directories for Java 1.6 or greater.
*
* @return a Set of java extension directory files
*/
private Set<File> findJavaExtensionDirectories() {
// set of all Java extension directories on this machine
Set<File> extDirs = findAllJavaExtensionDirectories();
// set of extension directories to remove
Set<File> toExclude = new TreeSet<File>();
synchronized(extDirs) {
for (File next: extDirs) {
if (next.getParentFile()==null || next.getParentFile().getParentFile()==null) continue;
File javaFile = next.getParentFile().getParentFile();
int n = javaFile.getPath().indexOf("jdk"); //$NON-NLS-1$
n = Math.max(n, javaFile.getPath().indexOf("jre")); //$NON-NLS-1$
int oldVersion = javaFile.getPath().indexOf("1.5."); //$NON-NLS-1$
oldVersion = Math.max(oldVersion, javaFile.getPath().indexOf("-5-")); //$NON-NLS-1$
oldVersion = Math.max(oldVersion, javaFile.getPath().indexOf("1.4.")); //$NON-NLS-1$
oldVersion = Math.max(oldVersion, javaFile.getPath().indexOf("1.3.")); //$NON-NLS-1$
oldVersion = Math.max(oldVersion, javaFile.getPath().indexOf("1.2.")); //$NON-NLS-1$
oldVersion = Math.max(oldVersion, javaFile.getPath().indexOf("1.3.")); //$NON-NLS-1$
oldVersion = Math.max(oldVersion, javaFile.getPath().indexOf("1.2.")); //$NON-NLS-1$
if (n>-1 && oldVersion>-1) {
toExclude.add(next);
}
}
extDirs.removeAll(toExclude);
vmsFound -= toExclude.size()+1;
}
return extDirs;
}
/**
* Finds all java extension directory files (recursively) in a directory.
* Extension directories are added to a collection of Files and returned.
* Does not search symbolic links.
*
* @param dir the directory
* @param extDirs the collection to add to
* @return the collection
*/
private Set<File> findJavaExtensionDirectories(File dir, Set<File> extDirs) {
if (dir==null) return extDirs;
try { // don't search symlinks
if (!dir.getCanonicalPath().equals(dir.getAbsolutePath())) {
return extDirs;
}
} catch (IOException e) {
}
// search all children contained in the directory
String[] fileNames = dir.list();
if (fileNames!=null && fileNames.length>0) {
for (String next: fileNames) {
File subDir = new File(dir, next);
// if subdirectory is an extensions folder, add it
if (extFilter.accept(subDir, subDir.getName())) {
synchronized(extDirs) {
extDirs.add(subDir);
vmsFound++;
}
}
// else search the next level down
else {
findJavaExtensionDirectories(subDir, extDirs);
}
}
}
return extDirs;
}
/**
* Returns the bitness (32 or 64) of a JRE path.
* @param jrePath the JRE path
* @return the bitness
*/
public boolean is32BitVM(String jrePath) {
if (OSPRuntime.isWindows()) {
String x86 = System.getenv("ProgramFiles(x86)"); //$NON-NLS-1$
if (x86!=null) {
return jrePath.contains(x86)? true: false;
}
// if x86 not defined, must be a 32-bit OS and VM?
return true;
}
if (OSPRuntime.isMac()) {
// all OSX VMs can be run in 32-bit mode?
return true;
}
// on linux, assume all JREs have same bitness as current VM?
return OSPRuntime.getVMBitness()==32;
}
/**
* Returns public JREs of a given bitness (32 or 64).
* @param vmBitness the bitness desired
* @return a Set of java JRE directory paths
*/
public TreeSet<String> getPublicJREs(int vmBitness) {
TreeSet<String> jreDirs = getAllJREs(vmBitness);
if (OSPRuntime.isWindows()) {
for (Iterator<String> it=jreDirs.iterator(); it.hasNext();) {
String next = it.next();
if (next.indexOf("jdk")>-1) { //$NON-NLS-1$
it.remove();
}
}
}
// log results for trouble-shooting
StringBuffer buf =new StringBuffer(vmBitness+"-bit JREs: "); //$NON-NLS-1$
for (String next: jreDirs) {
buf.append(next+", "); //$NON-NLS-1$
}
OSPLog.fine(buf.toString());
return jreDirs;
}
/**
* Returns all public and private JREs of a given bitness (32 or 64).
* @param vmBitness the bitness desired
* @return a Set of java JRE directory paths
*/
public TreeSet<String> getAllJREs(int vmBitness) {
// first look at extension directories
Set<File> extDirs = findJavaExtensionDirectories();
TreeSet<String> jreDirs = new TreeSet<String>();
try {
String x86 = System.getenv("ProgramFiles(x86)"); //$NON-NLS-1$
// iterate through extDirs to fill jreDirs
for (File next: extDirs) {
// move up two levels from lib/ext
File javaFile = next.getParentFile().getParentFile();
if (OSPRuntime.isWindows()) {
// if 32-bit VM specified, eliminate 64-bit
if (vmBitness==32 && x86!=null && !next.getPath().contains(x86)) {
continue;
}
// if 64-bit VM specified, eliminate 32-bit
if (vmBitness==64 && x86!=null && next.getPath().contains(x86)) {
continue;
}
javaFile = new File(javaFile, "bin/java.exe"); //$NON-NLS-1$
if (!javaFile.exists()) continue;
// ignore symlink files
try {
if (!javaFile.getCanonicalPath().equals(javaFile.getAbsolutePath()))
continue;
} catch (IOException e) {
}
String jrePath = OSPRuntime.getJREPath(javaFile);
jreDirs.add(jrePath);
}
else {
javaFile = new File(javaFile, "bin/java"); //$NON-NLS-1$
if (!javaFile.exists()) continue;
String jrePath = OSPRuntime.getJREPath(javaFile);
// only Apple-installed VMs (in /System/Library) support 32-bit operation
if (OSPRuntime.isMac() && vmBitness==32 && !jrePath.startsWith("/System/Library")) //$NON-NLS-1$
continue;
jreDirs.add(jrePath);
}
}
} catch (Exception e) {
}
// OSX: search for jres in /Library and /System/Library
// typical: /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
// typical: /System/Library/Java/JavaVirtualMachines/1.6.0jdk/Contents/Home
if (OSPRuntime.isMac()) {
Set<File> homeDirs = new TreeSet<File>();
String[] libraries = new String[] {"/System/Library", "/Library"}; //$NON-NLS-1$//$NON-NLS-2$
for (int i=0; i<libraries.length; i++) {
File library = new File(libraries[i]);
if (library.exists()) {
// search children
String[] subDirs = library.list();
if (subDirs!=null && subDirs.length>0) {
for (String next: subDirs) {
File nextFile = new File(library, next);
if (next.contains("Java")) { //$NON-NLS-1$
findJREHomes(nextFile, homeDirs);
}
// if subdirectory is an extensions folder, add it
else if (nextFile.getName().contains("Internet Plug-Ins")) { //$NON-NLS-1$
String[] children = nextFile.list();
if (children!=null && children.length>0) {
for (String childName: children) {
if (childName.contains("Java")) { //$NON-NLS-1$
findJREHomes(new File(nextFile, childName), homeDirs);
}
}
}
}
}
}
} // end library search
}
for (File home: homeDirs) {
// only Apple-installed VMs (in /System/Library) support 32-bit operation
if (vmBitness==32 && !home.getAbsolutePath().startsWith("/System/Library")) //$NON-NLS-1$
continue;
jreDirs.add(home.getAbsolutePath());
}
} // end OSX search
// log results for trouble-shooting
StringBuffer buf =new StringBuffer(vmBitness+"-bit JREs: "); //$NON-NLS-1$
for (String next: jreDirs) {
buf.append(next+", "); //$NON-NLS-1$
}
OSPLog.fine(buf.toString());
return jreDirs;
}
/**
* Finds java jre (home directory) files (recursively) in a directory.
* JRE homes are added to a collection of Files and returned.
* Does not search symbolic links.
*
* @param parent the directory
* @param jreHomes the collection to add to
* @return the collection
*/
private void findJREHomes(File parent, Set<File> jreHomes) {
if (parent==null || !parent.exists() || !parent.isDirectory() || jreHomes==null) return;
try { // don't search symlinks
if (!parent.getCanonicalPath().equals(parent.getAbsolutePath()))
return;
} catch (IOException e) {
}
// check this (parent) file
File javaFile = new File(parent, "bin/java"); //$NON-NLS-1$
if (javaFile.exists()) {
jreHomes.add(parent);
return;
}
// look for Contents if parent is plugin
if (parent.getName().contains(".plugin")) { //$NON-NLS-1$
File child = new File(parent, "Contents"); //$NON-NLS-1$
if (child.exists()) {
findJREHomes(new File(child, "Home"), jreHomes); //$NON-NLS-1$
}
return;
}
// search children
String[] children = parent.list();
if (children!=null) {
for (String childName: children) {
findJREHomes(new File(parent, childName), jreHomes);
}
}
}
/**
* Finds the default JRE of a given bitness (32 or 64). A public JRE is returned if possible.
* @param vmBitness the bitness desired
* @return the default JRE directory path, or null if none found
*/
public String getDefaultJRE(int vmBitness) {
// first look at public JREs
String JRE = null;
Set<String> jreDirs = getPublicJREs(vmBitness);
for (String next: jreDirs) {
// choose the last one (highest version) since they are in alpha/numerical order
JRE = next;
}
// if none found, look for jdk
if (JRE==null) {
jreDirs = getAllJREs(vmBitness);
for (String next: jreDirs) {
// choose the last one (highest version) since they are in alpha/numerical order
JRE = next;
}
}
// log results for trouble-shooting
OSPLog.fine(vmBitness+"-bit default JRE: "+JRE); //$NON-NLS-1$
return JRE;
}
/**
* Copies a source file to a target file.
*
* @param inFile the source
* @param outFile the target
* @return true if successfully copied
*/
private boolean copyFile(File inFile, File outFile) {
byte[] buffer = new byte[100000];
try {
InputStream in = new FileInputStream(inFile);
OutputStream out = new FileOutputStream(outFile);
while (true) {
synchronized (buffer) {
int amountRead = in.read(buffer);
if (amountRead == -1) {
break;
}
out.write(buffer, 0, amountRead);
}
}
in.close();
out.close();
// following line sometimes fails on Windows 7??
outFile.setLastModified(inFile.lastModified());
}
catch (IOException ex) {
return false;
}
return true;
}
/**
* ExtensionsFilter identifies Java extensions directories.
*
* Windows:
* typical jdk: Program Files\Java\jdkX.X.X_XX\jre\lib\ext
* typical jre: Program Files\Java\jreX.X.X_XX\lib\ext
* and Program Files\Java\jreX\lib\ext
* on 64-bit Windows, 32-bit VMs are in: Program Files (x86)\Java\...
* non-jre: Sun\Java\lib\ext.
* exclude: \Program Files (x86)\Java\Java3D\x.x.x\lib\ext
* jre search in: \Java
*
* OS X:
* typical: /System/Library/Java/JavaVirtualMachines/X.X.X.jdk/Contents/Home/lib/ext
* non-jre: /Library/Java/Extensions and/or /System/Library/Java/Extensions
* jre search in: /JavaVirtualMachines
*
* Linux:
* typical: /usr/lib/jvm/java-X-openjdk/jre/lib/ext
* or /usr/lib/jvm/java-X.X.X-openjdk/jre/lib/ext
* or /usr/lib/jvm/java-X-sun-X.X.X.XX/jre/lib/ext
* or /usr/lib/jvm/java-X.X.X-sun/jre/lib/ext
* non-jre: /usr/java/packages/lib/ext
* jre search in: /jvm
*/
static class ExtensionsFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
if (!dir.isDirectory()) return false;
// standardize paths to forward slash
String path = XML.forwardSlash(dir.getPath());
// accept jre extensions directories on all platforms
if (path.endsWith("/lib/ext")) { //$NON-NLS-1$
if (path.endsWith("/jre/lib/ext")) { //$NON-NLS-1$
return true;
}
String jre = XML.getName(path.substring(0, path.length()-8));
if (jre.indexOf("jre")>-1) //$NON-NLS-1$
return true;
if (path.indexOf("jdk")>-1 && path.indexOf("/Java/")>-1) //$NON-NLS-1$ //$NON-NLS-2$
return true;
if (path.indexOf("jre")>-1 && path.indexOf("/Java/")>-1) //$NON-NLS-1$ //$NON-NLS-2$
return true;
}
// accept non-jre extensions directories
// Linux
if (path.endsWith("java/packages/lib/ext")) return true; //$NON-NLS-1$
// Windows
if (path.endsWith("Java/lib/ext")) return true; //$NON-NLS-1$
// Mac OSX
if (path.endsWith("Java/Extensions")) return true; //$NON-NLS-1$
return false;
}
}
}