/*
* Copyright (C) 2010 reuillon
*
* 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.openmole.misc.pluginmanager.internal;
import java.io.File;
import java.io.FileFilter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections15.BidiMap;
import org.apache.commons.collections15.bidimap.DualHashBidiMap;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.service.packageadmin.ExportedPackage;
import org.openmole.misc.exception.InternalProcessingError;
import org.openmole.misc.pluginmanager.IPluginManager;
public class PluginManager implements IPluginManager {
final String defaultPatern = ".*\\.jar";
BidiMap<Bundle, File> files = new DualHashBidiMap<Bundle, File>();
Map<Bundle, Iterable<Bundle>> resolvedBundles = new HashMap<Bundle, Iterable<Bundle>>();
@Override
public synchronized Bundle load(File path) throws InternalProcessingError {
try {
Bundle b = Activator.getContext().installBundle(path.toURI().toString());
if (!resolvedBundles.containsKey(b)) {
b.start();
files.put(b, path);
resolvedBundles.put(b, getDependancies(b));
}
return b;
//allBundles.add(b);
} catch (BundleException ex) {
throw new InternalProcessingError(ex, ex.getLocalizedMessage());
}
}
@Override
public List<Bundle> loadDir(File path, final String pattern) throws InternalProcessingError {
return loadDir(path, new FileFilter() {
@Override
public boolean accept(File file) {
return file.isFile() && file.exists() && file.getName().matches(pattern);
}
});
}
@Override
public synchronized List<Bundle> loadDir(File path, FileFilter fiter) throws InternalProcessingError {
if (!path.exists() || !path.isDirectory()) {
return Collections.EMPTY_LIST;
}
try {
List<Bundle> bundles = new LinkedList<Bundle>();
for (File f : path.listFiles(fiter)) {
Bundle b = Activator.getContext().installBundle(f.toURI().toString());
if (!resolvedBundles.containsKey(b)) {
bundles.add(b);
files.put(b, f);
resolvedBundles.put(b, Collections.EMPTY_LIST);
}
}
for (Bundle b : bundles) {
b.start();
resolvedBundles.put(b, getDependancies(b));
}
/*for(Bundle b : Activator.getContext().getBundles()) {
System.out.println(b.getLocation());
}*/
return bundles;
//allBundles.add(b);
} catch (BundleException ex) {
throw new InternalProcessingError(ex, ex.getLocalizedMessage());
}
}
Set<Bundle> getDependancies(Bundle b) {
Set<Bundle> dependencies = new HashSet<Bundle>();
//TODO find a way to do that faster
for (Bundle bundle : resolvedBundles.keySet()) {
for (Bundle ib : getDependingBundles(bundle)) {
if (ib.equals(b)) {
dependencies.add(bundle);
for (Bundle depBun : getDependencies(bundle)) {
dependencies.add(depBun);
}
}
}
}
return dependencies;
}
//FIXME May not work with class in jars mentioned in Bundle-ClassPath : check it
/* Iterable<String> getClassNames(Bundle b) {
Collection<String> ret = new LinkedList<String>();
Stack<String> toExplore = new Stack<String>();
toExplore.add("");
while (!toExplore.isEmpty()) {
String currentDir = toExplore.pop();
Enumeration e = b.getEntryPaths(currentDir);
while (e.hasMoreElements()) {
String cur = e.nextElement().toString();
if(cur.endsWith("/")) {
toExplore.push(cur);
} else {
if(cur.endsWith(".class")){
String className = cur.substring(0, cur.length()-6).replace('/', '.');
ret.add(className);
}
}
}
}
return ret;
}*/
public synchronized Iterable<Bundle> getDependencies(Bundle b) {
if (resolvedBundles.containsKey(b)) {
return resolvedBundles.get(b);
} else {
return Collections.EMPTY_LIST;
}
}
@Override
public synchronized File getPluginForClass(Class c) {
Bundle b = Activator.getPackageAdmin().getBundle(c);
return files.get(b);
}
@Override
public synchronized Iterable<File> getPluginAndDependanciesForClass(Class c) {
Bundle b = Activator.getPackageAdmin().getBundle(c);
if (b == null) {
return Collections.EMPTY_LIST;
}
Collection<File> ret = new LinkedList<File>();
File plugin = files.get(b);
if (plugin != null) {
ret.add(plugin);
}
for (Bundle dep : getDependencies(b)) {
File depPlugin = files.get(dep);
if (depPlugin != null) {
ret.add(depPlugin);
}
}
return ret;
}
@Override
public Bundle load(String path) throws InternalProcessingError {
return load(new File(path));
}
@Override
public boolean isClassProvidedByAPlugin(Class c) {
boolean ret = resolvedBundles.containsKey(Activator.getPackageAdmin().getBundle(c));
return ret;
}
@Override
public synchronized void unload(File path) throws InternalProcessingError {
Bundle b = getBundle(path);
for (Bundle db : getDependingBundles(b)) {
unloadBundle(db);
}
unloadBundle(b);
}
private void unloadBundle(Bundle bundle) throws InternalProcessingError {
if (bundle != null) {
try {
bundle.uninstall();
files.remove(bundle);
resolvedBundles.remove(bundle);
} catch (BundleException ex) {
throw new InternalProcessingError(ex);
}
}
}
@Override
public synchronized Bundle getBundle(File path) throws InternalProcessingError {
return files.getKey(path);
}
private Iterable<Bundle> getDependingBundles(Bundle b) {
Collection<Bundle> ret = new LinkedList<Bundle>();
ExportedPackage[] exportedPackages = Activator.getPackageAdmin().getExportedPackages(b);
if (exportedPackages != null) {
for (ExportedPackage exportedPackage : exportedPackages) {
for (Bundle ib : exportedPackage.getImportingBundles()) {
ret.add(ib);
}
}
}
return ret;
}
@Override
public void unload(String path) throws InternalProcessingError {
unload(new File(path));
}
@Override
public Collection<Bundle> loadDir(File path) throws InternalProcessingError {
return loadDir(path, defaultPatern);
}
@Override
public Collection<Bundle> loadDir(String path) throws InternalProcessingError {
return loadDir(new File(path));
}
}