/**
* Copyright (C) 2005 - 2012 Eric Van Dewoestine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.eclim.plugin;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import java.util.Set;
import org.eclim.Services;
import org.eclim.command.Command;
import org.eclim.logging.Logger;
import org.eclim.util.IOUtils;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.scannotation.AnnotationDB;
/**
* Activator for eclim plugins.
*
* @author Eric Van Dewoestine
*/
public class Plugin
extends org.eclipse.core.runtime.Plugin
implements BundleListener
{
private static final Logger logger = Logger.getLogger(Plugin.class);
//The shared instance.
private static Plugin plugin;
public Plugin ()
{
plugin = this;
}
/**
* Returns the shared instance.
*/
public static Plugin getDefault()
{
return plugin;
}
/**
* {@inheritDoc}
* @see org.osgi.framework.BundleActivator#start(BundleContext)
*/
public void start(BundleContext context)
throws Exception
{
logger.debug("{}: start", context.getBundle().getSymbolicName());
super.start(context);
// start is called at startup regardless of whether Bundle-ActivationPolicy
// is lazy or not, but the bundle remains in the STARTING state and isn't
// activated until needed. So, to avoid loading eclim resouces at eclipse
// startup, we listen for the bundle to be activated and then load our
// resources.
context.addBundleListener(this);
}
/**
* Invoked when the bundle is activated by the bundle listener registered
* during start(BundleContext).
*
* @param context the BundleContext.
*/
public void activate(BundleContext context)
{
String name = context.getBundle().getSymbolicName();
logger.debug("{}: activate", name);
logger.debug("{}: loading plugin.properties", name);
Properties properties = new Properties();
InputStream in = null;
try{
in = context.getBundle().getResource("plugin.properties").openStream();
properties.load(in);
}catch(Exception e){
logger.error("Unable to load plugin.properties.", e);
//throw new RuntimeException(e);
}finally{
IOUtils.closeQuietly(in);
}
String resourceClass = properties.getProperty("eclim.plugin.resources");
logger.debug("{}: loading resources: {}", name, resourceClass);
Bundle bundle = this.getBundle();
try{
PluginResources resources = (PluginResources)
bundle.loadClass(resourceClass).newInstance();
logger.debug("{}: initializing resources", name);
resources.initialize(name);
Services.addPluginResources(resources);
loadCommands(bundle, resources);
}catch(Exception e){
logger.error("Error starting plugin: " + name, e);
}
}
/**
* {@inheritDoc}
* @see org.osgi.framework.BundleActivator#stop(BundleContext)
*/
public void stop(BundleContext context)
throws Exception
{
super.stop(context);
PluginResources resources = Services.removePluginResources(
Services.getPluginResources(context.getBundle().getSymbolicName()));
resources.close();
}
/**
* Given plugin resources instance, finds and loads all commands.
*
* @param bundle The Bundle.
* @param resources The PluginResources.
*/
private void loadCommands(Bundle bundle, PluginResources resources)
{
String name = this.getBundle().getSymbolicName();
try{
Class<?> rclass = resources.getClass();
ClassLoader classloader = rclass.getClassLoader();
String resourceName = rclass.getName().replace('.', '/') + ".class";
URL resource = classloader.getResource(resourceName);
String url = resource.toString();
url = url.substring(0, url.indexOf(resourceName));
String jarName = resources.getName().substring("org.".length()) + ".jar";
URL jarUrl = FileLocator.toFileURL(
FileLocator.find(bundle, new Path(jarName), null));
logger.debug("{}: loading commands", name);
AnnotationDB db = new AnnotationDB();
db.setScanClassAnnotations(true);
db.setScanFieldAnnotations(false);
db.setScanMethodAnnotations(false);
db.setScanParameterAnnotations(false);
db.scanArchives(jarUrl);
Set<String> commandClasses = db.getAnnotationIndex()
.get(org.eclim.annotation.Command.class.getName());
if(commandClasses != null){
for (String commandClass : commandClasses){
logger.debug("{}: loading command: {}", name, commandClass);
@SuppressWarnings("unchecked")
Class<? extends Command> cclass = (Class<? extends Command>)
classloader.loadClass(commandClass);
resources.registerCommand(cclass);
}
}else{
logger.debug("{}: no commands found", name);
}
}catch(Throwable t){
logger.error("Unable to load commands.", t);
}
}
/**
* {@inheritDoc}
* @see BundleListener#bundleChanged(BundleEvent)
*/
public void bundleChanged(BundleEvent event)
{
Bundle bundle = event.getBundle();
if (this.getBundle().getSymbolicName().equals(bundle.getSymbolicName())){
if (logger.isDebugEnabled()){
String state = "unknown";
switch(bundle.getState()){
case Bundle.ACTIVE:
state = "active";
break;
case Bundle.INSTALLED:
state = "installed";
break;
case Bundle.RESOLVED:
state = "resolved";
break;
case Bundle.STARTING:
state = "starting";
break;
case Bundle.STOPPING:
state = "starting";
break;
case Bundle.UNINSTALLED:
state = "starting";
break;
}
logger.debug("{}: bundleChanged: {}", bundle.getSymbolicName(), state);
}
if (bundle.getState() == Bundle.ACTIVE) {
this.activate(bundle.getBundleContext());
}
}
}
}