Archive for the category “Java”
Advanced features in Eclipse popup menus – Take 2: The new API
Following Robin’s comment on my earlier post I found the new API (Extension point description of org.eclipse.ui.menus) which unifies all command contributions to Eclipse menus, views, objects, toolbars etc. More »
Adding EOperation body to model
This is just a short notice for an EMF code generator feature which is mostly unknown and not really documented (well, it will be in the EMF 2nd Edition book, but this won’t be out until December or later).
To add a custom body (replacing the usual throw new UnsupportedOperationException body generated by default), follow this step-by-step howto.
Using advanced features in Eclipse popup menus
One of the first Hello World examples when dealing with contributions to Eclipse extension points is adding an objectContribution or a viewerContribution to a popup menu. However, if one wants to add a more flexible contribution which is hided or disabled in certain circumstances, documentation gets rare.
This article describes how to implement these advanced featues by example. Let’s assume we have an EMF model of objects MyObject which implement a property called type. EMF can generate item and label providers for tree editors. Let’s assume we have implemented such an editor and we want to contribute an action which is only visible for a particular type value.
Update: This article uses the old API of contributing actions to views’ and objects’ popup menus. There is a new API which is described in another post.
How to make an Eclipse Plugin Scriptable with Groovy
Say, you want to implement a quick and flexible automation enhancement to one of your eclipse RCP plugins – for example, because you are writing some sort of business application in which you always have to create a few standard structures manually in order to play with it and test it.
Here’s how you can do it the groovy way:
- create a new plugin project that depends on all plug-ins you want the automation script to be able to access.
- download
groovy-all-x.y.z.jarfrom the Groovy homepage and put it into your plug-in’s runtime classpath. - create an objectContribution like this:
<objectContribution
adaptable="false"
id="myproject.groovyRunner"
nameFilter="*.groovy"
objectClass="org.eclipse.core.resources.IFile">
<action
class="myproject.automate.RunGroovyAction"
enablesFor="1"
id="myproject.groovyRunnerAction"
label="Execute as Automation Script"
menubarPath="additions">
</action>
</objectContribution>
package myproject.automate;
import java.io.PrintWriter;
import java.io.StringWriter;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.codehaus.groovy.control.messages.Message;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import myproject.MyPlugin;
public class RunGroovyAction implements IObjectActionDelegate {
/** store the parent shell */
private Shell shell = null;
/** store the selected file */
private IFile file;
@Override
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
shell = targetPart.getSite().getShell();
}
@Override
public void selectionChanged(IAction action, ISelection selection) {
file = (IFile) ((IStructuredSelection)selection).getFirstElement();
}
@Override
public void run(IAction action) {
Assert.isNotNull(shell);
Assert.isNotNull(file);
try {
/* initialize the GroovyClassLoader with the Eclipse class loader */
GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader());
/* parse the groovy file */
Class<?> parsed = loader.parseClass(file.getContents());
/* cast it to runnable (or whatever you like to implement */
Class<? extends Runnable> clazz = parsed.asSubclass(Runnable.class);
/* instantiate and rum */
Runnable r = clazz.newInstance();
r.run();
}
catch(MultipleCompilationErrorsException e) {
/* report compilation errors in an error dialog */
StringWriter messages = new StringWriter();
PrintWriter printWriter = new PrintWriter(messages);
ErrorCollector ec = e.getErrorCollector();
if(ec.hasErrors()) {
for(Object o : ec.getErrors()) {
((Message) o).write(printWriter);
printWriter.append('\n');
}
}
Status status = new Status(IStatus.ERROR, MyPlugin.PLUGIN_ID,
messages.toString(), e);
ErrorDialog.openError(shell, "Compilation errors", messages.toString(), status);
} catch(Exception e) {
/* report all other errors here */
Status status = new Status(IStatus.ERROR, MyPlugin.PLUGIN_ID,
"Execution error", e.getCause() == null ? e : e
.getCause());
ErrorDialog.openError(shell, "Error", "Execution error", status);
}
}
}
Now you can run your Eclipse application, create a .groovy class (implementing Runnable in the case above) in a file and run it from within your runtime workspace.
Happy scripting …
Eclipse ClassLoading Hell
I like Eclipse. It is a really useful tool. It does a lot of magic behind the scenes and after a bit of getting familiar with its API you are able to write quite powerful plug-ins.
The bad thing is, that if something bad happens deep inside that magical box, it is quite challenging to analyze and understand what exactly is going on.
My problem had to do with such a case. I am using the Eclipse Modeling Framework (EMF) and my plan is to persist my models using the CDO-Teneo-Hibernate-MySQL bridge. CDO and Teneo are a bit experimental, but they are well supported in the eclipse.technology.emft Newsgroup.
The database persistence uses an approach of mapping arbitrary EMF models to database tables automatically including the dynamic creation of proxies. But as you might guess, all of this requires two sets of plug-ins (CDO and Teneo) as well as the hibernate library (which has further dependencies, e.g. the CGLib to dynamically create proxies) and the database driver library.
So, last week I ran into massive class loading problems, because the different plugins in the CDO and Teneo projects make use of the Hibernate libraries and manage access to them through a rather heterogeneous collection of exporting and importing packages and making use of the Eclipse-BuddyPolicy tag. Using this tag, for example, a user-contributed plugin can contribute the database driver class to the classpath of an existing plug-in.
However, one of the problems was caused by the fact, that the CGLib Java Bytecode generator was going to build a proxy for a class defined in another bundle (let’s say A) but at the same time also needed to incorporate the HibernateProxy interface (which was in the hibernate library in bundle B). And there is no dependency between A and B, because hibernate is a strategy add-on to CDO. Consequently, the class generation went awfully wrong because CGLib would take the classloader associated with its input class (so classloader of bundle A) which of course did not find the HibernateProxy interface from bundle B.
Back then I did not know all that. I only got an exception with a stack trace like this:
net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:317) at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.getProxyFactory(CGLIBLazyInitializer.java:127) at org.hibernate.proxy.pojo.cglib.CGLIBProxyFactory.postInstantiate(CGLIBProxyFactory.java:43) at org.hibernate.tuple.entity.PojoEntityTuplizer.buildProxyFactory(PojoEntityTuplizer.java:162) at org.hibernate.tuple.entity.AbstractEntityTuplizer.(AbstractEntityTuplizer.java:135) at org.hibernate.tuple.entity.PojoEntityTuplizer.(PojoEntityTuplizer.java:55) at org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping.(EntityEntityModeToTuplizerMapping.java:56) at org.hibernate.tuple.entity.EntityMetamodel.(EntityMetamodel.java:302) at org.hibernate.persister.entity.AbstractEntityPersister.(AbstractEntityPersister.java:434) at org.hibernate.persister.entity.JoinedSubclassEntityPersister.(JoinedSubclassEntityPersister.java:91) at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFactory.java:58) at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:226) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1300) at org.eclipse.emf.cdo.server.internal.hibernate.HibernatePackageHandler.getSessionFactory(HibernatePackageHandler.java:273) ... some more Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219) ... 74 more Caused by: java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(Unknown Source) ... 80 more Caused by: java.lang.ClassNotFoundException: org.hibernate.proxy.HibernateProxy at org.eclipse.osgi.framework.internal.core.BundleLoader.findClassInternal(BundleLoader.java:481) at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:397) at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:385) at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:87) at java.lang.ClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClassInternal(Unknown Source) ... 82 more
What …? I thought. CDO calls Hibernate calls CGLib and CGLib can not locate a Hibernate class? So I went digging through the internet tried to understand class loading in Eclipse I found a good article and this Eclipsepedia Entry which provide some insight.
However, this did not really help. If you are stuck somewhere with a ClassNotFoundException, because you don’t know, which class loader was being used to load the class (and failed), you have a problem. Both setting an exception breakpoint and stepping through the execution using the debugger is a bad idea, because there are so many class loads – and also many failing class loads – in Eclipse, that both methods are cumbersome.
A better way to gain information is enabling tracing and activating the class loader trace in org.eclipse.osgi:

This leads to output like this:
BundleClassLoader[org.eclipse.emf.teneo_0.8.0.qualifier].loadClass(org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue) BundleLoader[org.eclipse.emf.teneo_0.8.0.qualifier].loadBundleClass(org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue) BundleLoader[org.eclipse.emf.teneo.annotations_0.8.0.qualifier].findLocalClass(org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue) BundleLoader[org.eclipse.emf.teneo.annotations_0.8.0.qualifier] found local class org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue
So you can see in which bundle (in sqare brackets) the classloader looks for your class. This helped me find out, what was going on (described above). To solve my problem, I had to patch the two of the CDO manifests to make the classloader find the additional classes.
One way or another, all that classpath magic is hard to see through, when it comes to dynamic code generation and third party libraries in eclipse.
As a final note: I came over another article (in German), which does not directly address my issues, but could be helpful when you have several plug-ins which have to load different classes from different bundles.
