/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* 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 GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: PluginLookup.java,v 1.10 2007/03/28 16:05:16 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/
package jsynoptic.ui;
import java.awt.Window;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Logger;
import jsynoptic.base.Plugin;
import simtools.ui.BasicMessageWriter;
import simtools.ui.CheckBoxList;
import simtools.ui.ResourceFinder;
/**
* This class contains utilities to get/set dynamically the class path and the native library path
* It is strongly dependent on Sun's JDK implementation.
* Patches to support other JVMs welcome :)
*/
public class PluginLookup {
/**
* A looger to dump error or warning messages in a soket, an output stream, a file...
*/
static Logger _logger = simtools.util.LogConfigurator.getLogger(PluginLookup.class.getName());
/** Resources */
public static BasicMessageWriter messageWriter = ResourceFinder
.getMessages(JSynoptic.class);
protected static void testElement(Class theClass, String name, Vector results) {
if (name.equals("jsynoptic.base.Plugin")) return; // do not add base class!
try {
if (theClass.isAssignableFrom(Class.forName(name))) {
if (!results.contains(name)) results.add(name); // avoid duplicates
}
} catch (Throwable t) {
}
}
protected static void searchJar(Class theClass, JarFile j, Vector results) throws IOException {
Enumeration e = j.entries();
while (e.hasMoreElements()) {
JarEntry je =(JarEntry)e.nextElement();
if (je.isDirectory()) continue;
String entry = je.getName();
if (!entry.endsWith(".class")) continue;
entry = entry.substring(0,entry.length()-6); // removes .class
entry = entry.replace('\\','/');
entry = entry.replace('/','.');
testElement(theClass, entry,results);
}
}
protected static void searchFile(Class theClass, File d, Vector results, String prefix) {
File[] list = d.listFiles();
if (list==null) { // was a file!
list = new File[] {d};
}
for (int i=0; i<list.length; ++i) {
if (list[i].isDirectory() && (list[i].getName()!=".") && (list[i].getName()!="..")) {
searchFile(theClass, list[i],results,prefix.equals("")?list[i].getName():prefix+"."+list[i].getName());
continue;
}
String name = list[i].getName();
if (!name.endsWith(".class")) continue;
name = name.substring(0,name.length()-6); // removes .class
testElement(theClass, prefix+"."+name,results);
}
}
/**
* Public method to search for all implementors of a given class object, in the classpath
*/
public static Vector search(Class theClass) {
Vector entries = new Vector();
URL[] urls = sysloader.getURLs();
int i = 0;
for (i=0; i<urls.length; ++i) {
String elt;
try {
elt = URLDecoder.decode(urls[i].getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
elt = urls[i].getPath();
}
search(theClass,elt,entries);
}
return entries;
}
public static Vector search(String dirOrJar, Vector entries) {
return search(Plugin.class, dirOrJar, entries);
}
public static Vector search(Class theClass, String dirOrJar, Vector entries) {
JarFile j;
if (dirOrJar.endsWith(".jar") || dirOrJar.endsWith("jsynoptic") || dirOrJar.endsWith("jsynoptic.exe")) {
try {
j = new JarFile(dirOrJar);
} catch (IOException e) {
j = null;
}
} else j=null;
if (j!=null) {
try {
searchJar(theClass,j,entries);
j.close();
} catch (IOException e) {
}
}
else searchFile(theClass,new File(dirOrJar),entries,"");
return entries;
}
public static Vector lookup() {
Vector entries = new Vector();
URL[] urls = sysloader.getURLs();
for (int i=0; i<urls.length; ++i) {
String elt;
try {
elt = URLDecoder.decode(urls[i].getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
elt = urls[i].getPath();
}
search(elt,entries);
}
return entries;
}
private static final Class[] parameters = new Class[]{URL.class};
protected static String separator = System.getProperty("path.separator");
protected static URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
protected static Class sysclass = URLClassLoader.class;
/**
* Adds the given string to the classpath.
* A great thanks to java forum user 8025 = antony_miguel for dynamic classpath modification
*/
public static void addClassPath(String s) {
if (s==null) return;
addClassPath(new File(s));
}
/**
* Adds the given file to the classpath.
* A great thanks to java forum user 8025 = antony_miguel for dynamic classpath modification
*/
public static void addClassPath(File f) {
if (f==null) return;
try {
addClassPath(f.toURL());
} catch (MalformedURLException e) {
return;
}
}
/**
* Adds the given URL to the classpath.
* A great thanks to java forum user 8025 = antony_miguel for dynamic classpath modification
*/
public static void addClassPath(URL url) {
// Only add the URL if it does not exist yet
URL[] urls = sysloader.getURLs();
for (int i=0; i<urls.length; ++i) if (urls[i].equals(url)) return;
try {
Method method = sysclass.getDeclaredMethod("addURL",parameters);
method.setAccessible(true);
method.invoke(sysloader,new Object[]{ url });
} catch (Throwable t) {
//t.printStackTrace();
}//end try catch
}
/**
* Adds the given local path to the native library paths
*/
public static void addNativePath(File localPath) {
try {
Field field;
field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] libs = (String[])field.get(null);
// Only add the path if it does not exist yet
for (int i=0; i<libs.length; ++i) if (localPath.equals(new File(libs[i]))) return;
// OK, add the path
String[] newlibs = new String[libs.length+1];
for (int i=0; i<libs.length; ++i) newlibs[i] = libs[i];
newlibs[libs.length] = localPath.getAbsolutePath();
field.set(null, newlibs);
} catch (SecurityException e) {
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
/** Helper function to determine if a given file is a dynamic native library */
public static boolean isNativeLibrary(File f) {
String name = f.getName();
if (name.endsWith(".so") || name.endsWith(".dll") || name.endsWith(".jnilib"))
return true;
return false;
}
/**
* This method tries to automatically add the plugin paths
* It looks for the main class is in the classpath
* Then it adds the content of the "plugins" sirectory sibling to the core one, if it exists
* I also adds all sub-directories found inside it as class directories, and
* all jars found in the subdirectories in the class path too.
*/
public static void addPluginPaths() {
addPluginPaths(false, null);
}
/**
* This method tries to add the plugin paths
* It looks for the main class is in the classpath
* Then it adds the content of the "plugins" sirectory sibling to the core one, if it exists
* I also adds all sub-directories found inside it as class directories, and
* all jars found in the subdirectories in the class path too.
* @param userChoice if true then the user can confirm the selected paths
* @param parentWindow the owner for the dialog box displayed in interactive mode
* @return false if user selects cancel
*/
public static boolean addPluginPaths(boolean userChoice, Window parentWindow) {
Vector nativePaths=new Vector();
Vector classPaths=new Vector();
// first look for the Run class location
Vector entries = new Vector();
URL[] urls = sysloader.getURLs();
int i = 0;
String elt = "";
for (i=0; i<urls.length; ++i) {
try {
elt = URLDecoder.decode(urls[i].getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
elt = urls[i].getPath();
}
search(Run.class,elt,entries);
if (!entries.isEmpty()) break;
}
// if found then add the paths and jars wrt this location
if (i<urls.length) {
File parent = new File(elt).getParentFile();
if (parent!=null) {
// first add all jar siblings
// Also tests if parent contains libraries => add to native path
boolean nativePath = false;
File[] siblings = parent.listFiles();
if (siblings!=null) for(int j=0; j<siblings.length; ++j) {
if (siblings[j].getName().endsWith(".jar")){
classPaths.add(siblings[j]);
}
if ((!nativePath) && isNativeLibrary(siblings[j])) nativePath = true;
}
if (nativePath){
nativePaths.add(parent);
}
// then try to find a "plugins" directory, here or parent sibling
File plugs = new File(parent,"plugins");
if ((!plugs.exists()) && (parent.getParentFile()!=null)) {
plugs = new File(parent.getParentFile(),"plugins");
// Also tries to find a lib/jsynoptic directory, for unix bin/... lib/...
if (!plugs.exists()) {
plugs = new File(parent,"lib");
if (!plugs.exists()) {
plugs = new File(parent.getParentFile(),"lib"); // now in lib dir
if (!plugs.exists()) plugs = new File(parent,"ext"); // special for eclipse workspace
}
}
}
// Consider the plugins directory as classpath
// Consider the plugins directory sub-directories as classpath too, or go to subdirectory
// "jsynoptic" if it exists first
// Add all jar founds
if (plugs.exists()) {
classPaths.add(plugs);
// Go to subdirectory jsynoptic if it exists (in case of plugin dir is lib/jsynoptic)
// Also, on unix systems, don't add the whole system lib path as native libs : if
// the user wants that, (s)he should do it with LD_LIBRARY_PATH
File[] reps = plugs.listFiles();
if (reps!=null) for(int j=0; j<reps.length; ++j) {
if (reps[j].getName().equalsIgnoreCase("jsynoptic")) {
plugs = reps[j];
classPaths.add(plugs);
reps = plugs.listFiles();
break;
}
}
nativePath = false;
if (reps!=null) for(int j=0; j<reps.length; ++j) {
File[] maybejars = reps[j].listFiles();
if (maybejars!=null) {
boolean native2 = false;
classPaths.add(reps[j]); // Was a directory => add as classpath
for(int k=0; k<maybejars.length; ++k) {
if (maybejars[k].getName().endsWith(".jar")){
classPaths.add(maybejars[k]);
}
if ((!native2) && isNativeLibrary(maybejars[k])) native2 = true;
}
if (native2) addNativePath(reps[j]);
// Also add jar files and check for native libraries
} else {
if (reps[j].getName().endsWith(".jar")){
classPaths.add(reps[j]);
}
if ((!nativePath) && isNativeLibrary(reps[j])) nativePath = true;
}
}
if (nativePath){
nativePaths.add(parent);
}
}
}
}
// Now the system directories are added. Also add the user directory
// See simtools.ui.UserProperties ans jsynoptic.ui.Run for consistency
String productName = "jsynoptic";
String home = System.getProperty(productName + ".home", System.getProperty("user.home", ""));
File file = new File(home, "." + productName);
if (file.isDirectory()) {
classPaths.add(file);
File[] reps = file.listFiles();
boolean nativePath = false;
if (reps!=null) for(int j=0; j<reps.length; ++j) {
File[] maybejars = reps[j].listFiles();
if (maybejars!=null) {
boolean native2 = false;
classPaths.add(reps[j]); // Was a directory => add as classpath
for(int k=0; k<maybejars.length; ++k) {
if (maybejars[k].getName().endsWith(".jar")){
classPaths.add(maybejars[k]);
}
if ((!native2) && isNativeLibrary(maybejars[k])) native2 = true;
}
if (native2) addNativePath(reps[j]);
// Also add jar files and check for native libraries
} else {
if (reps[j].getName().endsWith(".jar")){
classPaths.add(reps[j]);
}
if ((!nativePath) && isNativeLibrary(reps[j])) nativePath = true;
}
}
if (nativePath){
nativePaths.add(file);
}
}
// remove duplicates
for(int ic=0;ic<classPaths.size();ic++){
for(int jc=ic+1;jc<classPaths.size();jc++){
if(classPaths.get(ic).equals(classPaths.get(jc))){
classPaths.remove(jc);
}
}
}
for(int in=0;in<nativePaths.size();in++){
for(int jn=in+1;jn<nativePaths.size();jn++){
if(nativePaths.get(in).equals(nativePaths.get(jn))){
nativePaths.remove(jn);
}
}
}
// check wrt user
boolean canceled=false;
if(userChoice){
String[] files=new String[classPaths.size()];
boolean[] checked=new boolean[classPaths.size()];
for(int k=0;k<classPaths.size();k++){
files[k]=((File)classPaths.get(k)).getAbsolutePath();
checked[k]=true;
}
boolean[] res=CheckBoxList.openDialog(
messageWriter.print0args("confirmPluginsPath"),
parentWindow, files, checked);
if(res==null){
canceled=true;
}
else{
for(int k=classPaths.size()-1;k>=0;k--){
if(!res[k]){
classPaths.remove(k);
}
}
}
}
if(!canceled){
// set the pathes
for(int ic=0;ic<classPaths.size();ic++){
addClassPath((File)classPaths.get(ic));
}
// Remark : the native path is not yet confirmed by the user
for(int ic=0;ic<nativePaths.size();ic++){
addNativePath((File)nativePaths.get(ic));
}
}
return !canceled;
}
public static String getClassPath() {
String ret = null;
URL[] urls = sysloader.getURLs();
for (int i=0; i<urls.length; ++i) {
if (ret==null) ret = "";
else ret += separator;
try {
ret += URLDecoder.decode(urls[i].getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
ret += urls[i].getPath();
}
}
return ret;
}
public static String getNativePath() {
String ret = null;
try {
Field field;
field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] libs = (String[])field.get(null);
for (int i=0; i<libs.length; ++i) {
if (ret==null) ret = "";
else ret += separator;
ret += libs[i];
}
} catch (SecurityException e) {
} catch (NoSuchFieldException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
return ret;
}
public static void main(String args[]) {
Vector entries = new Vector();
URL[] urls = sysloader.getURLs();
for (int i=0; i<urls.length; ++i) {
String elt;
try {
elt = URLDecoder.decode(urls[i].getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
elt = urls[i].getPath();
}
//String elt = urls[i].getPath();
_logger.fine("searching: "+elt);
search(elt,entries);
}
//entries = search(LookAndFeel.class);
_logger.fine("Found: ");
for (int i=0; i<entries.size(); ++i) _logger.fine(entries.get(i).toString());
_logger.fine("Looking for plugin dirs");
addPluginPaths();
entries = new Vector();
urls = sysloader.getURLs();
for (int i=0; i<urls.length; ++i) {
String elt;
try {
elt = URLDecoder.decode(urls[i].getPath(), "UTF-8");
} catch (UnsupportedEncodingException e) {
elt = urls[i].getPath();
}
_logger.fine("searching: "+elt);
search(elt,entries);
}
//entries = search(LookAndFeel.class);
_logger.fine("Found: ");
for (int i=0; i<entries.size(); ++i) _logger.fine(entries.get(i).toString());
_logger.fine("Native path: "+getNativePath());
}
}