Eclipse RCP Inject into instance of IWizard - eclipse

I am trying to inject some of my Wizard's fields.
I can successfully inject my own OSGi DS using the following helper class:
public class UtilRCP {
public static void inject(Plugin plugin, Object object) {
IEclipseContext serviceContext = EclipseContextFactory.getServiceContext(plugin.getBundle().getBundleContext());
ContextInjectionFactory.inject(object, serviceContext);
}
}
Other Services from the RCP ecosystem such as PartService or MApplication fail to be injected (null / no actual value was found for the argument "MApplication").
Here is the code
public class MyWizard extends MyAbstractWizard implements IImportWizard {
private MyWizardPage page;
#Inject
private EPartService partService;
#Inject
private DatabaseProvider databaseProvider;
#Inject
private MApplication application;
public MyWizard() {
System.err.println("Create");
System.err.println(databaseProvider);
System.err.println(partService);
System.err.println(application);
}
#Override
public void init(IWorkbench workbench, IStructuredSelection selection) {
UtilRCP.inject(Activator.getDefault(), this);
System.err.println("Init");
System.err.println(databaseProvider);
System.err.println(partService);
System.err.println(application);
}
#Override
public void addPages() {
super.addPages();
page = new MyWizardPage();
addPage(page);
}
#Override
public boolean performFinish() {
return true;
}
}

The service context has very limited contents and is not suitable for use like this.
In a 3.x style wizard like this you can get the workbench context from the IWorkbench object using:
#Override
public void init(IWorkbench workbench, IStructuredSelection selection) {
IEclipseContext context = (IEclipseContext)workbench.getService(IEclipseContext.class);
Note that when a dialog is active there is no active 'part' (because a dialog is not a part). This can cause problems with various APIs. In particular the application (workbench) part service will give an exception complaining that there is no active part.
You can get a working part service by explicitly getting the part service for the top level window using:
#Inject
MApplication application;
#Inject
EModelService modelService;
MWindow window = (MWindow)modelService.find("top level window id", application);
EPartService partService = window.getContext().get(EPartService.class);
I believe the top level window id for a 3.x RCP is "IDEWindow".
If its an E4 application, you can find the main window id in your Application.e4xml.

Related

How to get workbench/view in Eclipse (e4)?

I'm developing an Eclipse plugin in which I need to access a view.
I've found this way to get the workbench, but I guess it only worked in Eclipse 3.x and I am using Eclipse 4 so it doesn't work:
IWorkbench workbench = PlatformUI.getWorkbench();
IViewPart part = workbench.getActiveWorkbenchWindow().getActivePage()
.findView("id_of_the_view");
I then found this way:
#Inject
private static EPartService epartService;
MPart mPart = epartService.findPart("id_of_the_view");
MyViewClass part = (MyViewClass)mPart.getObject();
which throws a NullPointerException. I've made sure the view is open.
What am I missing?
Edit:
I've also tried this, as found here (german)
#Inject
private static EPartService epartService;
#Inject
private static MApplication application;
#Inject
private static EModelService modelService;
...
MUIElement element = modelService.find("id_of_the_view", application);
if(element instanceof MPart) {
MPart part = (MPart) element;
...
}
but I also get a NPE in the .find()-line.
You're right. In E4 you should use injection.
To avoid NPEs, you can get the reference to the IEclipseContext in part constructor.
public class MyPart {
private IEclipseContext context;
#Inject
public MyPart(IEclipseContext context) {
this.context = context;
}
}
Assuming you have your part class defined in a plugin, and that the class is referenced as Part in an E4 Application, you can get a reference to the Part instance using its id
Somewhere in your code you use the id to get an instance of your part
// somewhere in your code ..
// (where you can access the context instance)
MPart myPart = getPart(context, "com.example.myPart");
The easy way is to use EPartService.findPart() , as follows:
/** with this method I can get reference to the part */
public static MPart getPart(IEclipseContext context, String partId) {
EPartService partService = context.get(EPartService.class);
MPart part = partService.findPart(partId);
return part;
}
Beware of static methods. Context Objects and static members are not friends !!
For multiple matches, or advanced searches, you can use EPartService.findElements(), as follows :
public static List<MPart> getParts(IEclipseContext context,
MWindow workbenchWindow, String partId) {
EPartService partService = context.get(EPartService.class);
EModelService modelService = context.get(EModelService.class);
List<MPart> parts =
modelService.findElements(workbenchWindow, partId, MPart.class,
null, EModelService.OUTSIDE_PERSPECTIVE
| EModelService.IN_ANY_PERSPECTIVE
| EModelService.IN_SHARED_AREA);
return parts;
}

How to inject objects of custom classes (in eclipse e4) into an Handler class (defined for a menu item in Application's XMI file)

I'm new to Eclipse e4 and I am trying to inject an object of my custom class into a Handler class like below :
public class MenuHandler {
#Inject
Test2 user;
#Execute
public void execute(MApplication app, EPartService partService, EModelService modelService) {
System.out.println(user.getUserName()); // DefaultUser
user.setUserName("anotherUser");
System.out.println(user.getUserName()); //anotherUser
}
}
#Creatable
public class Test2 {
private String userName = "DefaultUser";
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
Having this code and If I launch my RCP application, and upon clicking the menu item (defined in the 'Application.e4xmi' file) my handler class ('MenuHandler') is not getting executed. Whereas if I remove the #Inject annotation from the handler class (i.e.., upon removing #Inject Test2 user; ) then the handler class is getting executed without any issues.
I think some problem exists if I have the annotation "#Inject" inside the Handler class.
Any suggestion will be greatly appreciated !
Maybe you should try to create and inject your custom object in the LifeCycleManager of your e4 application.
public class LifeCycleManager {
#PostContextCreate
public void postContextCreate(IEclipseContext context) {
final MCustomContext customContext = PersistenceUtils.load(MCustomContext.class);
context.set(MCustomContext.class, customContext);
}
}
This works fine for me.

How to Inject EPartService

I am developing e4 application. I want to inject EPartService outside the Part and Handler
when i am injecting EPartService then i will get null pointer error
public class DisplayRuntimePart {
#Inject EPartService partService;
private void displayPart(){
MPart part=partService.findPart("com.rcpe4.myproject.part.datapart");
mpart.setVisible(true);
partService.showPart(mpart, PartState.CREATE);
}
}
I am also read this question but till not solve my problem E4 EPartService findPart() throwing java.lang.Null Pointer Exception
Edit
I am inject EPartService in Part class. Class URI in Application.e4xml is bundleclass://com.abc.test/com.abc.test.part.MyPart in this class I am write code as follows.
Class Mypart{
#Inject EPartService prtservice;
#Inject
public MyPart() {
}
#PostConstruct
public void postConstruct(Composite parent) {
parent.setLayout(new FillLayout(SWT.HORIZONTAL));
htmlBrowser = new Browser(parent, SWT.NONE);
}
#PreDestroy
public void preDestroy() {
}
#Focus
public void onFocus() {
}
#Persist
public void save() {
}
public dispalyPart(){
MPart mpart=partService.findPart("com.abc.test.part.datapart"); **Here Getting Null Pointer Exception**
mpart.setVisible(true);
partService.showPart(mpart, PartState.CREATE);
}
}
Eclipse only does direct injection on objects that it 'knows' about - basically objects mentioned in the application model (e4xmi) files or created using something like EPartService.showPart.
If you want to do direct injection on objects that you create then you need to create them using ContextInjectionFactory. For example:
#Inject IEclipseContext context;
...
MyClass myClass = ContextInjectionFactory.make(MyClass.class, context);
you can also do injection on a class created in the normal way with:
ContextInjectionFactory.inject(myClass, context);
(this will not do injection on the constructor).
Note: Since this code is using direct injection you must run it from a class that the Eclipse application model does know about such as a command handler or an MPart.

How to pass additional data to GWT sub-editors?

i have this issue:
I have a PresenterWidget which contains sub-editors.
There are "container" elements which should be editable by this widget. These containers can be assigned to groups. To do so, i would like to fetch a list of all available groups from the server. So the widget is set up like this (i use GWTP):
public class ContainerEditorDialogPresenterWidget extends PresenterWidget<ContainerEditorDialogPresenterWidget.MyView> implements
ContainerEditorDialogUiHandlers {
private final PlaceManager placeManager;
private List<GroupDTO> groupList = new ArrayList<GroupDTO>();
private final DispatchAsync dispatcher;
#Inject
ContainerEditorDialogPresenterWidget(EventBus eventBus,
MyView view, PlaceManager placeManager, DispatchAsync dispatcher) {
super(eventBus, view);
getView().setUiHandlers(this);
this.dispatcher = dispatcher;
fetchGroups();
}
...
public void fetchGroups(){
FetchGroupsAction action = new FetchGroupsAction();
dispatcher.execute(action, new AsyncCallbackImpl<FetchGroupsResult>() {
#Override
public void onSuccess(FetchGroupsResult result) {
groupList = result.getGroupDtos();
eventBus.fireEvent(new GroupListUpdatedEvent(groupList));
}
});
}
So i call fetchGroups in the constructor to get it as early as possible. Since it is an AynchCallback, i get the result back "at some time". I then try to pass the values to the sub-editor with a GroupListUpdatedEvent. In there i have a Editor declared like this:
public class GroupListEditor extends Composite implements
IsEditor<ListEditor<String, GroupItemEditor>> {
private static StringListEditorUiBinder uiBinder = GWT
.create(StringListEditorUiBinder.class);
interface StringListEditorUiBinder extends
UiBinder<Widget, GroupListEditor> {
}
//Gives us access to the event bus.
#Inject private EventBus eventBus;
...
public GroupListEditor() {
initWidget(uiBinder.createAndBindUi(this));
eventBus.addHandler(GroupListUpdatedEvent.TYPE, new GroupListUpdatedEvent.GroupListUpdatedHandler() {
#Override
public void onGroupListUpdatedEvent(GroupListUpdatedEvent event) {
Log.debug("onContainerUpdatedEvent caught");
allGroups = event.getGroupList();
if(allGroups != null) {
for (GroupDTO g : allGroups) {
lbAllGroups.addItem(g.getName(), g.getId().toString());
}
lbAllGroups.setVisibleItemCount(5);
Log.debug("Item list = " + lbAllGroups.getItemCount());
} else {
Log.debug("GROUP LIST is Null!");
}
}
});
}
When i try to register the handler, i get an exception. So i expect the eventBus is not injected properly. What do i miss, how can i use events and the event bus if i am not in a Presenter?
And: Is this the right way at all to populate Editors with "utility" data? I guess Editor should be related directly to the data they care for. But how do i handle this kind of supplemental data?
Thanks :)
Do you use #UiField in your ContainerEditorDialogPresenterWidgetView for your GroupListEditor ?
If so then Dependency Injection won't work because you basically manually create the GroupListEditor which leads to EventBus being NULL.
I would also use Constructor Injection instead of field injection.
GroupListEditor:
#Inject
public GroupListEditor(EventBus eventBus) {
this.eventBus = eventBus;
}
ContainerEditorDialogPresenterWidgetView:
public class ContainerEditorDialogPresenterWidgetView {
#UiField(provided=true)
GroupListEditor groupListEditor;
#Inject
public ContainerEditorDialogPresenterWidgetView(GroupListEditor groupListEditor);
this.groupListEditor = groupListEditor;
initWidget();
}
}
Alternatively you could get an instance of your GroupListEditor via the Ginjector directly.

GIN & GWT: Binding Presentation layer with View

I'm trying to bind a GWT view with its presentation layer, but it doesn't seem to be doing anything.
It's a Spring Roo GWT generated project and I'm trying to use the scaffold given as far as possible.
The view is a simple button (R.ui.xml) and the rest of the view is defined in R.java:
public class R extends Composite implements RPresenter.Display {
interface MyUiBinder extends UiBinder<Widget, R> {}
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
#UiField Button myButton;
private ClickHandler buttonClickHandler = null;
public R(){
initWidget(uiBinder.createAndBindUi(this));
}
#UiHandler("myButton")
void onButtonClick(ClickEvent event){
GWT.log('Button clicked');
if (buttonClickHandler != null){
GWT.log("buttonClickHandler event triggered");
buttonClickHandler.onClick(event);
}
}
#Override
public void setButtonClickHandler(ClickHandler buttonClickHandler) {
GWT.log("setButtonClickHandler");
this.buttonClickHandler = buttonClickHandler;
}
}
The presenter:
public class RPresenter {
public interface Display extends IsWidget {
void setButtonClickHandler(ClickHandler buttonClickHandler);
}
private final Display display;
private final EventBus eventBus;
#Inject
public RPresenter(EventBus eventBus, Display display){
this.display = display;
this.eventBus = eventBus;
bind();
}
private void bind(){
display.setButtonClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
GWT.log("onClick event triggered");
}
});
}
public void go(HasWidgets container){
container.add(display.asWidget());
}
}
And for my GIN module I use the generated ScaffoldModule in the ...client.scaffold.ioc package:
public class ScaffoldModule extends AbstractGinModule {
#Override
protected void configure() {
GWT.log("ScaffoldModule configure");
bind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class);
bind(ApplicationRequestFactory.class).toProvider(RequestFactoryProvider.class).in(Singleton.class);
bind(PlaceController.class).toProvider(PlaceControllerProvider.class).in(Singleton.class);
//bind(RPresenter.Display.class).to(R.class).in(Singleton.class);
bind(RPresenter.Display.class).to(R.class);
}
static class PlaceControllerProvider implements Provider<PlaceController> {
private final EventBus eventBus;
#Inject
public PlaceControllerProvider(EventBus eventBus) {
this.eventBus = eventBus;
}
public PlaceController get() {
return new PlaceController(eventBus);
}
}
static class RequestFactoryProvider implements Provider<ApplicationRequestFactory> {
private final EventBus eventBus;
#Inject
public RequestFactoryProvider(EventBus eventBus) {
this.eventBus = eventBus;
}
public ApplicationRequestFactory get() {
ApplicationRequestFactory requestFactory = GWT.create(ApplicationRequestFactory.class);
requestFactory.initialize(eventBus);
return requestFactory;
}
}
}
In the GWT development mode console, the "ScaffoldModule configure" never displays, yet the generated scaffold seems to binding just fine as the events get passed along from component to component without a hitch, unless the binding is magically happening somewhere else and that is dead code.
When I put my bind(RPresenter.Display.class).to(R.class) in, it doesn't seem to do the binding. The only output I get in the GWT console is "Button clicked" which is called in the view and then nothing further. I'm clearly missing something, any ideas?
The call to GWT.log() will not output anything from an AbstractGinModule - classes that extend AbstractGinModule (ScaffoldModule in your situation) are used by gin at compile time to decide which concrete implementations to use for injected interfaces. From the rest of your description (i.e. that the UI shows up in the application) it appears that your dependency injection is working correctly.