package org.freehep.application.studio;
import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.freehep.application.ApplicationEvent;
import org.freehep.application.mdi.InternalFramePageManager;
import org.freehep.application.mdi.MDIApplication;
import org.freehep.application.mdi.PageContext;
import org.freehep.application.mdi.PageManager;
import org.freehep.application.mdi.TabbedPageManager;
import org.freehep.util.FreeHEPLookup;
import org.freehep.util.commandline.CommandLine;
import org.freehep.xml.util.ClassPathEntityResolver;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.openide.util.Lookup;
/**
*
* @author tonyj
* @version $Id: Studio.java 8584 2006-08-10 23:06:37Z duns $
*/
public class Studio extends MDIApplication
{
private FreeHEPLookup lookup;
private List loadedPlugins = new ArrayList();
private EventSender sender = new EventSender();
private boolean debugExtensions = System.getProperty("debugExtensions") != null;
private SAXBuilder builder;
private ExtensionClassLoader extensionLoader;
public static final String LOADDIR = "loaddir";
private boolean isApplicationVisible = false;
private boolean isApplicationInitialized = false;
private boolean atLeastOnePluginFailedToLoad = false;
protected Studio(String name)
{
super(name);
// For the moment at least we will use JDOM for parsing the plugin.xml files
builder = new SAXBuilder(true);
builder.setEntityResolver(new ClassPathEntityResolver("plugin.dtd",Studio.class));
getLookup().add(new TabbedPageManager(),"Tabbed Panes");
getLookup().add(new InternalFramePageManager(),"Internal Frames");
}
private Studio()
{
this("Studio");
}
protected FreeHEPLookup createLookup()
{
return FreeHEPLookup.instance();
}
public EventSender getEventSender()
{
return sender;
}
public FreeHEPLookup getLookup()
{
if (lookup == null) lookup = createLookup();
return lookup;
}
public void stopPlugin(PluginInfo plugin)
{
Plugin plug = plugin.getPlugin();
if (plug == null || !plug.canBeShutDown()) throw new IllegalArgumentException("Plugin can not be shutdown");
plug.stop();
plugin.setPlugin(null);
}
public void startPlugin(PluginInfo plugin) throws Throwable
{
Plugin plug = initializePlugin(plugin,extensionLoader);
revalidate();
}
private Plugin initializePlugin(PluginInfo plugin, ClassLoader loader) throws Throwable
{
try
{
getAppProperties().putAll(plugin.getProperties());
Class c = loader.loadClass(plugin.getMainClass());
Plugin plug = (Plugin) c.newInstance();
plug.setContext(this);
plugin.setPlugin(plug);
if (isApplicationInitialized) plug.postInit();
if (isApplicationVisible) plug.applicationVisible();
plugin.setErrorStatus(null);
return plug;
}
catch (Throwable t)
{
plugin.setErrorStatus(t);
throw t;
}
}
/**
* Return the list of installed plugins.
* Each element in the list will be an instance of PluginInfo
* @see PluginInfo
*/
public List getPlugins()
{
return loadedPlugins;
}
public static void main(String[] args)
{
new Studio().createFrame(args).setVisible(true);
}
protected CommandLine createCommandLine()
{
CommandLine cl = super.createCommandLine();
// register standard options
cl.addOption("extdir",null,"directory","Sets the directory to scan for plugins");
return cl;
}
protected void init()
{
super.init();
setStatusMessage("Loading extensions...");
loadExtensions();
// Now load the real page manager
setStatusMessage("Setting page manager...");
setPageManager(createRealPageManager());
if (atLeastOnePluginFailedToLoad) error("At least one plugin failed to load, see Plugin Manager for details");
}
protected PageManager createRealPageManager()
{
String name = getUserProperties().getProperty("pageManagerName","Tabbed Panes");
Lookup.Template template = new Lookup.Template(PageManager.class,name,null);
Lookup.Result result = getLookup().lookup(template);
if (!result.allInstances().isEmpty())
{
return (PageManager) result.allInstances().iterator().next();
}
else
{
// Previously we used the class name as pageManager, so this is just for
// backward compatibility.
try
{
return super.createPageManager();
}
catch (Throwable t)
{
// Last chance, use whatever page manager we can find.
PageManager pm = (PageManager) getLookup().lookup(PageManager.class);
if (pm != null) return pm;
return new TabbedPageManager();
}
}
}
private void loadExtensions()
{
Map extensionClasspath = new TreeMap();
Set plugins = new LinkedHashSet();
// We look for extensions:
// a) In the directory specified by the org.freehep.application.studio.user.extensions property
// b) In the directory specified by the org.freehep.application.studio.group.extensions property
// c) In the directory specified by the org.freehep.application.studio.system.extensions property
// The following defaults apply if the property is not specified
// a) {user.home}/.FreeHEPPlugins
// b) none
// c) {java.home}/.FreeHEPPlugins
// Note, we scan system dirs first, because files/plugins found later replace earlier ones
String[] extDirs =
{
getSystemExtensionsDir(),
getGroupExtensionsDir(),
getUserExtensionsDir()
};
scanExtensionDirectories(extDirs,extensionClasspath,plugins);
// Create the extension Loader.
URL[] urls = new URL[extensionClasspath.size()];
extensionClasspath.values().toArray(urls);
extensionLoader = new ExtensionClassLoader(urls);
createLookup().setClassLoader(extensionLoader);
// Make sure the extensionClassLoader is set as the contextClassLoader
// so that services etc can be looked up in jar files from the extension
// directory.
Runnable lola = new Runnable()
{
public void run()
{
Thread.currentThread().setContextClassLoader(extensionLoader);
}
};
lola.run(); // for the main (this) thread
SwingUtilities.invokeLater(lola); // for the event dispatch thread
// Now try loading the plugins
loadPlugins(new ArrayList(plugins),extensionLoader);
}
public List buildPluginList(InputStream in, File loadDir) throws IOException
{
Properties user = getUserProperties();
List result = new ArrayList();
try
{
Document doc = builder.build(in);
Element root = doc.getRootElement();
for (Iterator iter = root.getChildren().iterator(); iter.hasNext(); )
{
Element node = (Element) iter.next();
PluginInfo plugin = new PluginInfo(node);
plugin.setLoadDirectory(loadDir);
plugin.loadUserProperties(user);
result.add(plugin);
if (debugExtensions) System.out.println("\t\tPlugin: "+plugin.getName());
}
}
catch (JDOMException x)
{
if (debugExtensions) x.printStackTrace();
}
finally
{
in.close();
}
return result;
}
public void loadPlugins(List plugins, ClassLoader loader)
{
Iterator iter = plugins.iterator();
while (iter.hasNext())
{
PluginInfo plugin = (PluginInfo) iter.next();
loadedPlugins.add(plugin);
if (plugin.isLoadAtStart())
{
try
{
setStatusMessage("Loading "+plugin.getName()+"...");
if (debugExtensions) System.out.println("Loading plugin: "+plugin.getName());
long start = System.currentTimeMillis();
initializePlugin(plugin, loader);
plugin.setErrorStatus(null);
long stop = System.currentTimeMillis();
if (debugExtensions) System.out.println("Done loading in : "+(stop-start)+"ms");
}
catch (Throwable t)
{
if (debugExtensions) System.err.println("Unable to load plugin "+plugin.getName());
plugin.setErrorStatus(t);
atLeastOnePluginFailedToLoad = true;
}
}
}
// plugins may have added menus etc, so for good measure!
revalidate();
}
private void scanExtensionDirectories(String[] dirs, Map extensionClasspath, Set plugins)
{
for (int i=0; i<dirs.length; i++)
{
if (dirs[i] == null) continue;
File extdir = new File(dirs[i]);
if (debugExtensions) System.out.println("Seaching for extensions in: "+extdir);
if (extdir.isDirectory()) scanExtensionDirectory(extdir,extensionClasspath,plugins);
}
}
private void scanExtensionDirectory(File extdir, Map extensionClasspath, Set plugins)
{
String[] files = extdir.list();
for (int i=0; i<files.length; i++)
{
if (files[i].endsWith(".jar"))
{
// Try to open the jar file, and see if it has a plugin manifest
File f = new File(extdir,files[i]);
if( f.length() > 0)
{
try
{
if (debugExtensions) System.out.println("\tFound: "+files[i]);
JarFile jarFile = new JarFile(f);
JarEntry manifest = jarFile.getJarEntry("PLUGIN-inf/plugins.xml");
if (manifest != null )
{
InputStream in = jarFile.getInputStream(manifest);
List newPlugins = buildPluginList(in, extdir);
boolean rc = plugins.removeAll(newPlugins); // JAS-274
plugins.addAll(newPlugins);
}
jarFile.close();
extensionClasspath.put(f.getName(),f.toURI().toURL());
}
catch (IOException x)
{
System.err.println("Extension jar file "+files[i]+" could not be loaded"+x);
}
}
else
{ // Files with length 0 have been flagged for deletion by the plugin manager.
f.delete();
}
}
}
}
public String getUserExtensionsDir()
{
String extdir = getCommandLine().getOption("extdir");
if (extdir != null) return extdir;
return getAppProperties().getProperty("org.freehep.application.studio.user.extensions","{user.home}/.FreeHEPPlugins");
}
public String getGroupExtensionsDir()
{
return getAppProperties().getProperty("org.freehep.application.studio.group.extensions");
}
public String getSystemExtensionsDir()
{
return getAppProperties().getProperty("org.freehep.application.studio.system.extensions","{java.home}/FreeHEPPlugins");
}
public ExtensionClassLoader getExtensionLoader()
{
return extensionLoader;
}
protected void fireInitializationComplete(ApplicationEvent event)
{
super.fireInitializationComplete(event);
getEventSender().broadcast(event);
for (Iterator i = loadedPlugins.iterator(); i.hasNext(); )
{
PluginInfo info = (PluginInfo) i.next();
Plugin plugin = info.getPlugin();
if (plugin != null) plugin.postInit();
}
isApplicationInitialized = true;
}
protected void fireApplicationVisible(ApplicationEvent event)
{
super.fireApplicationVisible(event);
getEventSender().broadcast(event);
for (Iterator i = loadedPlugins.iterator(); i.hasNext(); )
{
PluginInfo info = (PluginInfo) i.next();
Plugin plugin = info.getPlugin();
if (plugin != null) plugin.applicationVisible();
}
isApplicationVisible = true;
}
protected void fireAboutToExit(ApplicationEvent event)
{
for (Iterator i = loadedPlugins.iterator(); i.hasNext(); )
{
PluginInfo info = (PluginInfo) i.next();
Properties user = getUserProperties();
info.saveUserProperties(user);
}
super.fireAboutToExit(event);
getEventSender().broadcast(event);
}
protected PageManager createPageManager()
{
// We initially create a dummy page manager, so we can delay creating the
// real page manager until after the plugins have been loaded (so that
// a plugin can register a plugin manager)
return new DummyPageManager();
}
private static class DummyPageManager extends PageManager
{
private JPanel panel = new JPanel();
protected Component getEmbodiment()
{
return panel;
}
protected void iconChanged(PageContext page)
{
}
protected void show(PageContext page)
{
}
protected void titleChanged(PageContext page)
{
}
}
}