On Thu, May 27, 2010 at 19:36, Andreas Truszkowski <[email protected]> wrote:
> if I try to install my plugin via "Find new plugins" the updates and
> plugins manager installs all dependencies without an error. But when I
> restart Taverna the plugin is not visible. Starting Taverna in debug
> mode shows also no errors or any sign for a problem. How can I find the
> reason why the plugin is not loaded accurate. Here is the URL to the plugin:
> http://www.ts-concepts.de/cdk-taverna2/plugin/

The log file in ~/.taverna-2.1.2/logs says:

INFO  2010-05-28 11:19:40,187
(net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanel:131) -
Updating My example service
..
INFO  2010-05-28 11:19:40,288
(net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanel:131) - Found
0 services My example service

.. so it is installed, just not listed under "Import new services"


>From what I see in
http://www.ts-concepts.de/cdk-taverna2/maven/release_repository/org/openscience/cdk/applications/taverna/cdk-taverna-2-activity-ui/0.1/cdk-taverna-2-activity-ui-0.1-sources.jar
in CDKServiceProvider:

   List<ServiceDescription> results = new ArrayList<ServiceDescription>();
            List<Class> classes =
CDKClassGrabber.getClassessOfSuperclass("org.openscience.cdk.applications.taverna",
                    AbstractCDKActivity.class);
            // Register activities
            for (Class<? extends AbstractCDKActivity> activityClass : classes) {
                AbstractCDKActivity activity = activityClass.newInstance();
                service = new CDKServiceDescriptor(activityClass);
                service.setActivityName(activity.getActivityName());
                service.setFolderName(activity.getFolderName());
                // TODO set description
                service.setDescription(activity.getDescription());

service.setConfigurationPanelClass(CDKClassGrabber.getClassByName("org.openscience.cdk.applications.taverna",
                        activity.getConfigurationPanelClass()));

service.setAdditionalProperties(activity.getAdditionalProperties());
                results.add(service);
            }

As this is returning 0 services, I believe that the list "classes" is empty.

It seems we are moving into the dark territory of classloaders. Here be dragons!


There seems to be a big hint that some Class.forName() or similar is
being done here in this CDKClassGrabber.getClassessOfSuperclass - but
remember that when installed as a plugin you will not have the CDK
classes on the System classloader, only on the classloader of your
plugin as you will now be running inside the plugin system Raven.

So it depends on how CDKClassGrabber looks up those classes. If it
tries to use ClassLoader.getSystemClassLoader() it would not find any
implementations, as this would only find classes in JARs that are in
Taverna's lib/ folder.

your CDKClassGrabber.getClassesForPackage does:

ClassLoader cld = Thread.currentThread().getContextClassLoader();

.. and tries to use this to look for URLs matching your package name.


While running from Eclipse this should work, as the context class
loader is the system class loader, which knows about everything on
your classpath, including the project with CDKClassGrabber etc. While
running in Taverna the default context class loader will just know
about the lib/ folder.


A quick fix would therefore be to change the context class loader in
CDKServiceProvider before calling CDKClassGrabber. This should be
fairly safe, as each service provider is run in a different thread.
Try something like:

Thread.currentThread().setContextClassLoader(getClass().getClassLoader());


Another important thing to know in this aspect is that Raven class
loaders are arranged hierarchical, one per POM file. Tthe classloader
for
cdk-taverna-2-activity-ui will search the classloaders for each of its
dependencies, so it will search cdk-taverna-2-activity, which then
again will search its dependencies, like org.openscience.cdk:cdk. But
it is important to know that this does not go backwards, so
cdk-taverna-2-activity is not able to find any classes in
cdk-taverna-2-activity-ui as it does not depend on it. (Doing so would
form a loop).

So if you wanted CDKClassGrabber in cdk-taverna-2-activity to find any
classes in cdk-taverna-2-activity-ui, then the only way would be
either to pass in the ClassLoader itself as a parameter, or to set the
context classloader and use that from within CDKClassGrabber.


Now as for the clever bit that looks at the JAR files, I would
recommend rather to go for the slightly more standard SPI approach as
you've already seen in
./META-INF/services/net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider
and 
./META-INF/services/net.sf.taverna.t2.workbench.ui.views.contextualviews.activity.ContextualViewFactory


So I would go for
META-INF/services/org.openscience.cdk.applications.taverna.AbstractCDKActivity
- and in that file(s) list:

org.openscience.cdk.applications.taverna.io.MDLSDFileWriterActivity
org.openscience.cdk.applications.taverna.io.SMILESFileReaderActivity

etc.

To find those implementations of AbstractCDKActivity you can use our
SPI registry, which would check all plugins (and not just your own)
for the SPI description files, and use the classloaders where it finds
such files to instantiate those activities.

We have two versions, one that can find the classes, and another one
that finds the classes and makes a (singleton) instance of that class.
As you'll

You can use it like this:

import net.sf.taverna.t2.spi.SPIRegistry;
..
protected SPIRegistry<AbstractCDKActivity> cdkActivityRegistry = new
SPIRegistry<AbstractCDKActivity>(
                        AbstractCDKActivity.class);

..
for (AbstractCDKActivity cdkActivity : cdkActivityRegistry.getInstances()) {
                service = new CDKServiceDescriptor(cdkActivity.getClass());
                service.setActivityName(activity.getActivityName());
                service.setFolderName(activity.getFolderName());
                service.setDescription(activity.getDescription());

service.setAdditionalProperties(activity.getAdditionalProperties());
                results.add(service);
}

If you go for this approach you would not need to use the package name
restrictions or the context loader tricks. You would still need to
sort something out about this service.setConfigurationPanelClass -
which I guess has to do with finding the configuration panel for your
activity.

You could do a similar interface and SPI registry for finding the
right ActivityConfigurationPanel - they would have to be
instantiatable (is that a word?) with a default constructor, and some
canHandle(AbstractCDKActivity activity) method which you check to be
able to find the right panel. We would normally do this as a factory
that could then return the ActivityConfigurationPanel.

On the other hand I'm not sure why this would be needed, it seems to
be because you have a common CDKContextualView which simply calls
these panels - I would just avoid this indirection and additional SPI
by having several contextual views and configuration actions, one
could say it handles activities MDLSDFileWriterActivity,
MDLMolFileWriterActivity, etc.  (or a common FileWriter interface?) -
and another for the FileReader.


These are just my suggestions, to get it working inside the plugin all
I believe you will need to do is set the Thread contextual view using
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());

.. I tested this for your code, and it seems to work, except I got
this exception:

org.openscience.cdk.applications.taverna.ui.serviceprovider.CDKServiceProvider
java.lang.ClassNotFoundException:
org.openscience.cdk.applications.taverna.ui.serviceprovider.CDKServiceProvider
        at 
net.sf.taverna.raven.repository.impl.LocalArtifactClassLoader.findClass(LocalArtifactClassLoader.java:343)
        at 
net.sf.taverna.raven.repository.impl.LocalArtifactClassLoader.findClass(LocalArtifactClassLoader.java:276)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at 
org.openscience.cdk.applications.taverna.CDKClassGrabber.getClassesForPackage(CDKClassGrabber.java:68)
        at 
org.openscience.cdk.applications.taverna.CDKClassGrabber.getClassessOfSuperclass(CDKClassGrabber.java:92)
        at 
org.openscience.cdk.applications.taverna.ui.serviceprovider.CDKServiceProvider.findServiceDescriptionsAsync(CDKServiceProvider.java:29)
        at 
net.sf.taverna.t2.servicedescriptions.impl.ServiceDescriptionRegistryImpl$FindServiceDescriptionsThread.run(ServiceDescriptionRegistryImpl.java:502)

Here, and a few other places you are using Java's Class.forName() when
loading a class, which tries to find the class loader via a hack of
looking in the stack to find what is the calling class's classLoader,
in this case it will be cdk-taverna-2-activity and not
cdk-taverna-2-activity-ui where
org.openscience.cdk.applications.taverna.ui.serviceprovider.CDKServiceProvider
lives. Remember that inside Raven cdk-taverna-2-activity can't see
cdk-taverna-2-activity-ui. (In this case you don't actually need
CDKServiceProvider - but your method is looking up ALL classes of the
given package name - another good hint to rather go for a more normal
SPI approach)

You will need to use the correct classloader, ie. the one where you
found the class (or SPI file). So try using cld.loadClass(className)
instead.

-- 
Stian Soiland-Reyes

------------------------------------------------------------------------------

_______________________________________________
taverna-hackers mailing list
[email protected]
Web site: http://www.taverna.org.uk
Mailing lists: http://www.taverna.org.uk/about/contact-us/
Developers Guide: http://www.taverna.org.uk/developers/

Reply via email to