/*
* Copyright (c) 2012. The Genome Analysis Centre, Norwich, UK
* MISO project contacts: Robert Davey, Mario Caccamo @ TGAC
* *********************************************************************
*
* This file is part of MISO.
*
* MISO 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.
*
* MISO 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 MISO. If not, see <http://www.gnu.org/licenses/>.
*
* *********************************************************************
*/
package uk.ac.bbsrc.tgac.miso.core.plugin.annotation;
//import uk.ac.ebi.arrayexpress2.annotation.ArrayExpress2Metadata;
//import uk.ac.ebi.arrayexpress2.annotation.ArrayExpress2Operation;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
/**
* uk.ac.bbsrc.tgac.miso.core.plugin.annotation
*
* Searches for MISO annotations and generates information about how
* to invoke the tools. This introspector can be used to examine jar files or
* the classpath.
* <p/>
* This introspector can also be used to generate a client stub, which can then
* be invoked directly from the command line using a client that binds arguments
* to the generated stubs.
*
* @author Tony Burdett
* @author Rob Davey
* @date 20-Jul-2010
* @since 0.0.2
*/
public class MisoPluginAnalyser {
/**
* Probes the set of currently loaded classes and uses the file
* "META-INF/miso/plugins" to discover those annotated with {@link
* MisoOperation} annotations. The
* plugins file is a SPI-type file, that lists all the annotated
* classes, and is generated by annotation processing at compile-time. Each
* resource thus annotated is used to extract an {@link
* MisoMetadata} object, and the set of all metadata returned.
*
* @return the metadata describing all MISO tools found
* @throws java.io.IOException if there was a problem reading from the
* resource "META-INF/miso/plugins"
* @throws ClassNotFoundException if a class described in one of the
* META-INF/miso/plugins was not be
* loaded
*/
// public Collection<MisoMetadata> analyse()
// throws IOException, ClassNotFoundException {
// return analyseByLoader(getClass().getClassLoader());
// }
/**
* Probes all classes in the supplied jar file and uses the file
* "META-INF/miso/plugins" to discover those annotated with {@link
* MisoOperation} annotations. The
* plugins file is a SPI-type file, that lists all the annotated
* classes, and is generated by annotation processing at compile-time. Each
* resource thus annotated is used to extract an {@link
* MisoMetadata} object, and the set of all metadata returned.
*
* @param pluginJarFile the jar file containing MISO tool(s) that we
* want to analyse
* @return the metadata describing all MISO tools found
* @throws java.io.IOException if there was a problem reading from the
* resource "META-INF/miso/plugins"
* @throws ClassNotFoundException if a class described in one of the
* META-INF/miso/plugins was not be
* loaded
*/
/* public Collection<MisoMetadata> analyse(File pluginJarFile)
throws IOException, ClassNotFoundException {
// load the supplied jar file
return analyseByLoader(new URLClassLoader(
new URL[]{pluginJarFile.getAbsoluteFile().toURI().toURL()}));
}
private Collection<MisoMetadata> analyseByLoader(ClassLoader loader)
throws IOException, ClassNotFoundException {
// collection of metadata objects to return
Set<MisoMetadata> metadata = new HashSet<MisoMetadata>();
// grab services-type file: this is a list of classes annotated with @MisoPlugin
Enumeration<URL> resources =
loader.getResources("META-INF/miso/plugins");
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
// read generator line
BufferedReader reader =
new BufferedReader(new InputStreamReader(resource.openStream()));
String line;
while ((line = reader.readLine()) != null) {
Class m = Class.forName(line.trim());
metadata.add(examineClass(m));
}
}
return metadata;
}
private MisoMetadata examineClass(Class misoPluginClass) {
MisoMetadataImpl misoMetadata = null;
// reflect class to discover operation name
if (misoPluginClass.isAnnotationPresent(MisoPlugin.class)) {
misoMetadata = new MisoMetadataImpl(misoPluginClass);
// reflect all methods on this class to discover operations
for (Method m : misoPluginClass.getMethods()) {
if (m.isAnnotationPresent(MisoOperation.class)) {
examineMethods(misoMetadata, m);
}
}
}
return misoMetadata;
}
private void examineMethods(MisoMetadataImpl misoMetadata,
Method misoOperationMethod) {
// create operation by checking annotations on the supplied method
MisoOperation misoOp =
misoOperationMethod.getAnnotation(MisoOperation.class);
// read operation metadata
final String opName = misoOp.name();
final String opDescription = misoOp.description();
final int parallelProcesses = misoOp.maxParallelProcesses();
final int memRequirement = misoOp.maxMemoryRequirement();
final Collection<MisoMetadata.Parameter> parameters =
new HashSet<MisoMetadata.Parameter>();
final String methodName = misoOperationMethod.getName();
// read parameter metadata
Annotation[][] parameterAnnotations =
misoOperationMethod.getParameterAnnotations();
for (int pos = 0; pos < parameterAnnotations.length; pos++) {
// check next argument for MisoParameter annotations
for (Annotation annotation : parameterAnnotations[pos]) {
if (annotation instanceof MisoParameter) {
// found annotation, create argument
MisoParameter misoP = (MisoParameter) annotation;
final String paramName = misoP.name();
final String paramDescription = misoP.description();
final int paramPosition = pos;
MisoMetadata.Parameter parameter =
new MisoMetadata.Parameter() {
@Override
public String getParameterName() {
return paramName;
}
@Override
public String getParameterDescription() {
return paramDescription;
}
@Override
public int getParameterPosition() {
return paramPosition;
}
};
parameters.add(parameter);
}
}
}
// create the operation
MisoMetadata.Operation operation =
new MisoMetadata.Operation() {
@Override
public String getOperationName() {
return opName;
}
@Override
public String getOperationDescription() {
return opDescription;
}
@Override
public int getMaxAllowedParallelProcesses() {
return parallelProcesses;
}
@Override
public int getMaxMemoryRequirement() {
return memRequirement;
}
@Override
public Collection<MisoMetadata.Parameter> getParameters() {
return parameters;
}
@Override public String getMethodName() {
return methodName;
}
};
misoMetadata.addOperation(operation);
}
private class MisoMetadataImpl implements MisoMetadata {
private Class misoPluginClass;
private String name;
private String description;
private Set<Operation> operations;
public MisoMetadataImpl(Class mpc) {
this.misoPluginClass = mpc;
// examine annotations, extract mode name
Annotation ann = mpc.getAnnotation(
MisoPlugin.class);
if (ann instanceof MisoPlugin) {
MisoPlugin mp = (MisoPlugin) ann;
this.name = mp.name();
this.description = mp.description();
this.operations = new HashSet<Operation>();
}
}
@Override
public String getToolName() {
return name;
}
@Override public String getToolDescription() {
return description;
}
@Override
public Collection<Operation> getOperations() {
return operations;
}
@Override public String getClassName() {
return misoPluginClass.getName();
}
public void addOperation(Operation operation) {
operations.add(operation);
}
}
*/
}