E4 - 在Eclipse 3.x 中使用E4的依赖注入[译]

当我们问及E4的Single Sourcing的时候:

   E4 可以运行在Eclipse 4.0 SDK和Eclipse 3.X SDK中。

 

在这篇文章中我们将会熟悉怎样将E4的依赖注入编程模型引入到普通的3.x平台中,我们将开发一个语言翻译的应用。

源代码托管在http://code.google.com/a/eclipselabs.org/p/eclipse-translator-view/.

要想容易读懂这篇文章,你得对RCP有较深的认识,熟悉Eclipse 3.x的ServiceLocators,SWT/JFace和数据绑定相关知识。

 

第一步:新建一个3.x的RCP ViewPart插件

   你可以通过PDE提供的模板创建一个“RCP application with a view"工程(如命名at.bestsolution.translate.view),运行后应该像这样子: 

   

 然后发挥你的动手能力,将它改成成这样吧:

    

下面是代码:

1 package at.bestsolution.translate.app;
2
3 import org.eclipse.jface.viewers.ComboViewer;
4 import org.eclipse.swt.SWT;
5 import org.eclipse.swt.layout.GridData;
6 import org.eclipse.swt.layout.GridLayout;
7 import org.eclipse.swt.widgets.Button;
8 import org.eclipse.swt.widgets.Composite;
9 import org.eclipse.swt.widgets.Label;
10 import org.eclipse.swt.widgets.Text;
11 import org.eclipse.ui.part.ViewPart;
12
13 public class View extends ViewPart {
14 public static final String ID = "at.bestsolution.translate.app.view";
15
16 private Text term;
17
18 /**
19 * This is a callback that will allow us to create the viewer and initialize
20 * it.
21 */
22 public void createPartControl(Composite parent) {
23     GridLayout layout = new GridLayout(2, false);
24     parent.setLayout(layout);
25
26     Label l = new Label(parent, SWT.NONE);
27     l.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true,false, 2, 1));
30     l.setText("Translator");
31
32     l = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
33     l.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true,false, 2, 1));
36
37     l = new Label(parent, SWT.NONE);
38     l.setText("Term");
39
40     term = new Text(parent, SWT.BORDER);
41     term.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
42
43     l = new Label(parent, SWT.NONE);
44     l.setText("Translator");
45
46     ComboViewer translator = new ComboViewer(parent);
47     translator.getControl().setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
50
51     l = new Label(parent, SWT.NONE);
52     l.setText("Language");
53
54     ComboViewer language = new ComboViewer(parent);
55     language.getControl().setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
58
59     Button b = new Button(parent, SWT.PUSH);
60     b.setText("Translate");
61     b.setLayoutData(new GridData(GridData.END, GridData.CENTER, false,false, 2, 1));
64
65     l = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
66     l.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true,false, 2, 1));
69
70     l = new Label(parent, SWT.NONE);
71     l.setText("Translation");
72 }
73
74    /**
75    * Passing the focus request to the viewer's control.
76    */
77   public void setFocus() {
78      term.setFocus();
79   }
80 }

第二步:创建翻译服务

创建Service-Interface-Bundle

创建一个空的插件工程,即没有任何依赖性.命名为”at.bestsolution.translate.services“,然后定义ITranslator接口

1 package at.bestsolution.demo.translate.services;
2
3 import java.lang.reflect.InvocationTargetException;
4
5 public interface ITranslator {
6    public class FromTo {
7       public final String from;
8       public final String to;
9
10      public FromTo(String from, String to) {
11        this.from = from;
12        this.to = to;
13      }
14    }
15
16    public String getName();
17    public FromTo[] getFromTo();
18
19    public String translate(FromTo fromTo, String term) throws InvocationTargetException;
20 }

实现ITranslator接口

在这个例子中我们使用http://code.google.com/p/google-api-translate-java/这个库。连接Google提供的翻译服务。

实现类如下:

1 package at.bestsolution.translate.services.google;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.util.ArrayList;
5
6 import at.bestsolution.translate.services.ITranslator;
7
8 import com.google.api.translate.Language;
9 import com.google.api.translate.Translate;
10
11 public class GoogleTranslator implements ITranslator {
12     private static FromTo[] FROM_TOS;
13
14    static {
15       ArrayList<FromTo> l = new ArrayList<FromTo>();
16       for(Language fromLang : Language.values()) {
17           for( Language toLanguage : Language.values() ) {
18                if( fromLang != toLanguage ) {
19                   l.add(new FromTo(fromLang.name(), toLanguage.name()));
20                }
21           }
22        }
23       FROM_TOS = l.toArray(new FromTo[0]);
24     }
25
26    public String getName() {
27        return "Google Translate";
28    }
29
30    public FromTo[] getFromTo() {
31         return FROM_TOS;
32    }
33
34     public String translate(FromTo fromTo, String term) throws InvocationTargetException {
35       try {
36             return Translate.execute(term, Language.valueOf(fromTo.from), Language.valueOf(fromTo.to));
37         } catch (Exception e) {
38             throw new InvocationTargetException(e);
39         }
40      }
41 }

然后注册到OSGI的DS中,在插件的OSGI-INF中

1 <?xml version="1.0" encoding="UTF-8"?>
2 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="at.bestsolution.translate.services.google.googletranslator">
3    <implementation class="at.bestsolution.translate.services.google.GoogleTranslator"/>
4    <service>
5      <provide interface="at.bestsolution.translate.services.ITranslator"/>
6    </service>
7 </scr:component>

接下来添加ITranslatorProviderService收集各种ITranslator翻译方式。定义如下:

package at.bestsolution.translate.services;

import org.eclipse.core.databinding.observable.list.IObservableList;

public interface ITranslatorProvider {
   public IObservableList getTranslators();
}

实现类:

package at.bestsolution.translate.services.internal;

import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;

import at.bestsolution.translate.services.ITranslator;
import at.bestsolution.translate.services.ITranslatorProvider;

public class TranslatorProviderComponent implements ITranslatorProvider {
     private IObservableList list = new WritableList(); 

     public IObservableList getTranslators() {
        return list;
     }

     public void addTranslator(final ITranslator translator) {
         list.getRealm().exec(
new Runnable() {
            public void run() {
                list.add(translator);
            }
         });
      }

     public void removeTranslator(final ITranslator translator ) {
          list.getRealm().exec(
new Runnable() {
             public void run() {
               list.remove(translator);
             }
          });
     }
}

注册成服务:

View Code
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr=http://www.osgi.org/xmlns/scr/v1.1.0 name="at.bestsolution.translate.services.translatorprovider">
  <implementation class="at.bestsolution.translate.services.internal.TranslatorProviderComponent"/>
    <service>
      <provide interface="at.bestsolution.translate.services.ITranslatorProvider"/>
    </service>
   <reference bind="addTranslator" cardinality="0..n" interface="at.bestsolution.translate.service.ITranslator" name="ITranslator" policy="dynamic" unbind="removeTranslator"/>
</scr:component>

将服务集成到UI程序中

先从OSGi-Service注册表中获取服务:

public class TranslatorView extends ViewPart {
   private IObservableList getTranslators() {
      Bundle bundle
= FrameworkUtil.getBundle(getClass());
      BundleContext context
= bundle.getBundleContext();
      ServiceReference reference
= context.getServiceReference(ITranslatorProvider.class.getName());
      ITranslatorProvider pv
= (ITranslatorProvider) context.getService(reference);
      return pv.getTranslators();
    }
}

接下来处理ITranslatorProvider 

public class TranslatorView extends ViewPart {
public void createPartControl(Composite parent) {
    // ....

   IObservableList translatorsObs
= getTranslators();

   ComboViewer translator
= new ComboViewer(parent);
   translator.getControl().setLayoutData(
new GridData(GridData.FILL_HORIZONTAL));
   translator.setContentProvider(
new ObservableListContentProvider());
   translator.setLabelProvider(
new LabelProvider() {
       @Override
       public String getText(Object element) {
          return ((ITranslator)element).getName();
       }
    });
    translator.setInput(translatorsObs);

     l
= new Label(parent, SWT.NONE);
     l.setText(
"Language");

     ComboViewer language
= new ComboViewer(parent);
     language.getControl().setLayoutData(
new GridData(GridData.FILL_HORIZONTAL));
      language.setContentProvider(
new ObservableListContentProvider());
     language.setLabelProvider(
new LabelProvider() {
          @Override
         public String getText(Object element) {
             return ((FromTo)element).from + " - " + ((FromTo)element).to;
         }
      });

      IListProperty fromToProp
= PojoProperties.list("fromTo");
      IValueProperty selectionProp
= ViewerProperties.singleSelection();

       IObservableList input
= fromToProp.observeDetail(selectionProp.observe(translator));
      language.setInput(input);

      // ....
    }
}

正如你所见,工作还比较顺利,但注册OSGI服务还是个比较麻烦的工作。

让我们开始领略一下E4的风采吧

Let's Start From Here - E4

将代码重构成POJO对象

E4的annotation依赖注入方式:

  • @Inject
  • @PostConstruct
  • 。。。。。

接下来我们需要将UI组件剥离成一个简单的POJO类,使得我们的ViewPart看起来像这样:

package at.bestsolution.translate.view;

import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.
*;

public class TranslatorView extends ViewPart {

public static final String ID = "at.bestsolution.translate.view.views.TranslatorView";

private TranslatorComponent component = new TranslatorComponent();

public void createPartControl(Composite parent) {
     component.createUI(parent);
}

public void setFocus() {
     component.setFocus();
}
}

接下来我们会使用E4的依赖注入服务帮助我们得到OSGI的服务。

我们必须让每个工作台实例都有自己的EclipseContext,这样需要在工作台级别初始化Eclipse的依赖注入.我们需要用到ui.services扩展点。

package at.bestsolution.translate.view;

import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.
internal.services.IWorkbenchLocationService;
import org.eclipse.ui.services.AbstractServiceFactory;
import org.eclipse.ui.services.IServiceLocator;

@SuppressWarnings(
"restriction")
public class DIContextServiceFactory extends AbstractServiceFactory {

   @Override
   public Object create(@SuppressWarnings("rawtypes") Class serviceInterface, IServiceLocator parentLocator,IServiceLocator locator) {
      if( ! IEclipseContextProvider.class.equals(serviceInterface) ) {
            return null;
       }

       Object o
= parentLocator.getService(serviceInterface);

       IWorkbenchLocationService wls
= (IWorkbenchLocationService) locator.getService(IWorkbenchLocationService.class);
       final IWorkbenchWindow window
= wls.getWorkbenchWindow();
       final IWorkbenchPartSite site
= wls.getPartSite();

       if( window == null && site == null ) {
            return new IEclipseContextProviderImpl();
      }
else if( o != null && site == null ) {
            return new IEclipseContextProviderImpl((IEclipseContextProvider) o);
      }

       return o;
    }
}

工作区服务:

package at.bestsolution.translate.view;

import org.eclipse.e4.core.contexts.IEclipseContext;

public interface IEclipseContextProvider {
    public IEclipseContext getContext();
}

实现类:

package at.bestsolution.translate.view;

import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;

public class IEclipseContextProviderImpl implements IEclipseContextProvider {
    private IEclipseContext context;

    public IEclipseContextProviderImpl(IEclipseContextProvider parent) {
        this.context = parent.getContext().createChild();
     }

     public IEclipseContextProviderImpl() {
         Bundle bundle
= FrameworkUtil.getBundle(IEclipseContextProviderImpl.class);
         BundleContext bundleContext
= bundle.getBundleContext();
         IEclipseContext serviceContext
= EclipseContextFactory.getServiceContext(bundleContext);

         context
=  serviceContext.createChild("WorkbenchContext"); //$NON-NLS-1$
     }

      public IEclipseContext getContext() {
         return context;
      }
}

使用依赖注入规则重构ViewPart的POJO对象

public class TranslatorComponent {
private Text term;
private ComboViewer translator;

@Inject
public TranslatorComponent(Composite parent) {
     createUI(parent);
}

// ...
}

将@Inject标记到setTranslationProvider方法中,以便createUI方法可以获取到对象,然后删除OSGI服务,简化成了:

@Inject
void setTranslationProvider(@Optional ITranslatorProvider provider) {
    if( provider == null ) {
        if( ! translator.getControl().isDisposed() ) {
            translator.setInput(
new WritableList());
        }
    }
else {
         translator.setInput(provider.getTranslators());
      }
}

采用这种可以让ITranslatorProvider实例注册到OSGI注册表,

最后调整ViewPart

public class TranslatorView extends ViewPart {

public static final String ID = "at.bestsolution.translate.view.views.TranslatorView";

      private IEclipseContext context;
      private TranslatorComponent component;

      public TranslatorView() {
      }

      public void createPartControl(Composite parent) {
          IEclipseContextProvider p
= (IEclipseContextProvider) getSite().getService(IEclipseContextProvider.class);
          context
= p.getContext().createChild();
          context.set(Composite.
class, parent);
          component
= ContextInjectionFactory.make(TranslatorComponent.class, context);
       }

      /**
       * Passing the focus request to the viewer's control.
         */
      public void setFocus() {
         component.setFocus();
       }

      @Override
       public void dispose() {
          context.dispose();
          super.dispose();
      }
}

done!

http://tomsondev.bestsolution.at/2010/05/29/e4-singlesourcing-part-1/
 

posted @ 2011-07-12 18:40  vwpolo  阅读(1561)  评论(0编辑  收藏  举报