How to get/inject EPartService outside part or in LifeCycle Manager or How to control life cycle of e4 RCP app in true sense? - eclipse-rcp

My application consists of many parts and they are defined in application's e4xmi file. I want to hide and show them dynamically. I am using EpartService to do so in handlers, where I can inject it.
But I also want to control the show/hide of parts with something like life cycle manager, where I can not inject EPartService. Is there any way to achieve and fully control RCP application's life cycle?
There seems the exact same question here and void of solution:
https://www.eclipse.org/forums/index.php/t/595958/
I want to implement 'remember me like feature' where part having sign in screen is shown instead of other parts. Also after log out same sign-in part is to be shown. So I need to control life cycle of RCP app. But I cant inject EPartService before anything in Application's e4xmi is initiated.

If you are creating a class from something which is injected (such as the LifeCycle class) you can create your class with injection using ContextInjectionFactory:
#Inject
IEclipseContext context;
MyClass myClass = ContextInjectionFactory.make(MyClass.class, context);
Or if you just pass an IEclipseContext to the class you can get the part service using:
EPartService partService = context.get(EPartService.class);
Note: There is a separate instance of the part service for each part. Depending on what you are doing you may need to make sure you have the service for the active part.

If you are not locked in to using SWT, you could use the e(fx)clipse e4 renderer for JavaFX instead.
e(fx)clipse has more possibilities to control the lifecycle of the application. For example you can return a Boolean from #PostContextCreate to signal whether you want to continue the startup or not. You will not be able to use EPartService here though, but you can roll your own login dialog using dependency injection as greg-449 has described it in his answer.
public class StartupHook {
#PostContextCreate
public Boolean startUp(IEclipseContext context) {
// show your login dialog
LoginManager loginManager = ContextInjectionFactory.make(LoginManager.class, context);
if(!loginManager.askUserToLogin()) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}
(You can also restart the application. Form
more details see http://tomsondev.bestsolution.at/2014/11/03/efxclipse-1-1-new-features-api-to-restart-your-e4-app-on-startup/).

Related

How to switch between UI implementations in run-time?

I'm developing an application using GWT 2.6.
Now I have a task to implement a functionality to switch between simple/expert modes (of view) at run-time. It means to show a simplified version of view which may contain another widgets.
But the problem is that all the views in most cases are "static" and defined as singletons (using GIN).
Is it possible to implement this using only GWT and what should I use to make it?.
There are many ways that this can be done. A couple of ideas that spring to mind:
Option 1.
Instead of using GIN to inject the view, inject a view provider (com.google.inject.Provider). This can be used by the code that builds the view (the activity?) to get the view. The implementation of the view provider can then return an implementation of the view for the relevant mode, simple or expert. For example,
#Inject
public ComposeMessageActivity(Provider<ComposeMessageView> viewProvider, ...) { ... }
#Override
public final void start(final AcceptsOneWidget panel, final EventBus eventBus) {
view = viewProvider.get();
Option 2.
Use deferred binding. This is not so dynamic but has the advantage a simple implementation will be a smaller download than an expert implementation. There is a project, gwt-seminar, on github that shows this in practice that has mobile and desktop versions.

Refresh Eclipse 4 RCP view on wizard perform finish

Rookie question that I'm not having much luck with. In my e4 RCP application, I have a couple of instances where I create an object in a wizard that should then appear in one of my views.
The desired behavior is similar to how the eclipse Package Explorer View updates after a new project is created.
I was thinking I could just grab the view from the partService and run my own update method:
MPart ingredientsView = partService.showPart("com.personal.recipes.part.ingredientsview", PartState.ACTIVATE);
IngredientsView iv = (IngredientsView) ingredientsView.getObject();
iv.updateView();
While this works in other places, when called from a wizard 'partService' is null and the app NPE's out.
So what is the proscribed method of forcing e4 views to update after modifying their contents?
EDIT:
I tried to use the ContextInjectionFactory like #greg-449 showed in his answer, but I'm uncertain where to place it in my code, or how to define the context. I'm launching the wizard from a toolbar button, and placed the following code in my handler:
#Execute
public void execute(Shell shell) {
IEclipseContext context = EclipseContextFactory.create();
IWizard ingredientWizard = ContextInjectionFactory.make(IngredientWizard.class, context);
WizardDialog wizardDialog = new WizardDialog(shell, ingredientWizard);
wizardDialog.open();
}
However, when I tried to get the part service with #Inject EPartService partService; I got an InjectionException saying no error was found.
Once injection is available, using the EventBroker looks like the way to go.
enter code hereThe best way to update a view is to use a model for the content of the view. Your wizard seems to allow editing or creating ingredients. When you perform the finish of your wizard you are probably modifying some ingredient data. The ingredient model should be informed of these changes. If the view uses a content provider that observes this model is will update automatically when the model sees the update (this is the observer pattern).
How this works depends on the nature of your data. You could use the PropertyChange-Support in Java.
To do so let the content provider implement the org.eclipse.jface.util.IPropertyChangeListener interface and fire property change events when the data is changed.
UPDATE
My ContentProvider implements the property change interface. Whenever a property change event is received the viewer is refreshed(asynchronously). All my persistence operations are handled by data managers similar to Fowler's the table data gateway pattern but sometimes for more than one table. The data manager fires the property change event. This way the UI (wizard) does not need to know about persistence
Injection is only done on objects that the application model knows about. So it is not done on Wizards or Dialogs unless you do it 'manually' using ContextInjectionFactory when you create the dialog:
IWizard wizard = ContextInjectionFactory.make(YourWizardClass.class, eclipseContext);
WizardDialog dialog = new WizardDialog(shell, wizard);
This will do injection on your wizard class giving you access to the EPartService.
You could also use the 'event broker' (IEventBroker) to broadcast an event to anything that is interested rather than finding your specific view.

Is this an acceptable GWT solution

I want to create a GWT UI where I basically will have a single HTML page that loads a PanelA object. The user will then do their thing and eventually perform an action that will move them onto another view/screen.
I have simplified my existing views to contain just a single button which moves the user onto the next page etc. for simplicity I only have 2 views to start.
Here is my start up entry point.
public class StockWatcher implements EntryPoint
{
public void onModuleLoad()
{
final RootPanel rootPanel = RootPanel.get();
rootPanel.add( PanelA.getInstance() );
}
}
Here is the PanelA class
public class PanelA extends HTMLPanel
{
private static PanelA panel;
private PanelA()
{
super("Panel A");
final RootPanel rootPanel = RootPanel.get();
Button btnNewButton = new Button("Go to panel B");
btnNewButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event)
{
rootPanel.clear();
rootPanel.add( PanelB.getInstance() );
}
});
add(btnNewButton);
}
public static PanelA getInstance()
{
if (panel == null)
{
panel = new PanelA();
}
return panel;
}
}
My other PanelB class is pretty much the same as PanelA , ie button that brings me back to PanelA
My UI works as desired.
My Question is, Is this singleton type pattern a correct or proper way to do this? ie Have a stack of Singleton UI views that get popped on/off the main panel?
Also, what is the best way to handle hitory/breadcrumb trace through a GWT app, ie allowing a user to go back to the previous screen bearing in mind that they may navigate to PanelX from either of PanelA, PanelB or PanelC
I use "Activities and Places" to manage all of this, and it's been working quite well in production for a year or so.
https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces
I think it's fine to use a Singleton mechanism for your views, but you have to make sure to completely reset any state you store. For me, it was easier to just create new views every time the user navigates to a new spot, and then if I detected a problem with load times or something to retroactively cause the view to re-use its components. I'd advise you to get the navigation working first, and then worry about the singleton (or not) optimizations.
I recommend Activities and Places design pattern.
It covers all the issues you raise in your question, plus many more that you have not thought of (or did not ask about) yet, like native browser history management and ability to bookmark different places within the app, handling of page reloads, efficient memory management vs optimized DOM operations, scalability (building an app with dozens/hundreds of views with minimal code duplication), testability, and code-splitting (where to split large apps).
I suggest you refer "GWTP" framework to make a GWT Project, some of the features currently supported by GWTP:
Dependency injection through GIN and Guice;
Simple but powerful history management mechanism;
Support for nested presenters;
Lazy instantiation for presenter and view;
Effortless and efficient code splitting;
Integrated command pattern supporting undo/redo;
So your questions and query like Singleton UI views, best way to handle hitory/breadcrumb trace and singleton type pattern , will cover and one good framework will make easy project management.

Dealing with model save and update with GWT Platform

I'm trying to adapt my GWT web application from my home-grown MVC to GWT Platform.
I've managed to port the my application views with presenters, and essentially able to access views via PlaceRequest. And with changing the URL (#).
However I am not sure how to deal with Models using this GWT platform, in the common MVP I know there is a go() method in the presenter which fetches data, say from server via RPC.
In the GWT platform presenter here are the methods automatically generated by the Eclipse plugin:
Constructor
revealInParent
onBind
onReset
Where should I put the RPC code that will fetch and update my model. Say in the presenter I have:
ProfilePresenter.java:
public class ProfilePresenter
extends
Presenter<ProfilePresenter.MyView, ProfilePresenter.MyProxy> {
public interface MyView extends View {
HasText getFullname();
HasText getLocation();
HasText getAboutme();
HasText getLastlogin();
}
private User user; // Model which represents the User information etc.
And when the View associated with the Presenter is shown I need to fetch the User model from the server and update the model and then subsequently update the view through the interfaces it expose.
Also, say I have some buttons in the view, which then can be accessed by the presenter through HasClickHandler where should I put the event handlers?
I would put the RPC call in the onReset method.
See the presenter lifecycle
Personally I deal with events using the reversed MVP pattern. But you can also call a handler this way:
getView().getSubmitButton().addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
}
});
with the following signature for getSubmitButton in your view interface:
HasClickHandlers getSubmitButton()
Sydney covered most of your questions.
In general onResetmethod is a good place to make backend calls.
Sometimes when the backend call takes longer and you want to display the view only after the data was loaded you can use manual reveal.
But for the profile page I don't think that is necessary.
I also agree with the reverse MVP pattern. It's way easer to test presenters using the reverse MVP Pattern than using the HasXXXHandlers interfaces.

Programmatically showing a View from an Eclipse Plug-in

I have a plug-in to an Eclipse RCP application that has a view. After an event occurs in the RCP application, the plug-in is instantiated, its methods are called to populate the plug-in's model, but I cannot find how to make the view appear without going to the "Show View..." menu.
I would think that there would be something in the workbench singleton that could handle this, but I have not found out how anywhere.
You are probably looking for this:
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView("viewId");
If called from handler of a command
HandlerUtil.getActiveWorkbenchWindow(event).getActivePage().showView(viewId);
would be better, as I know.
I found the need to bring the view to the front after it had been opened and pushed to the background. The activate method does the trick.
PlatformUI.getWorkbench()
.getActiveWorkbenchWindow()
.getActivePage()
.activate(workbenchPartToActivate);
NOTE: The workbenchPartToActivate is an instance of IWorkbenchPart.
In e4, the EPartService is responsible for opening Parts. This can also be used to open e3 ViewParts. Instantiate the following class through your IEclipseContext, call the openPart-Method, and you should see the Eclipse internal browser view.
public class Opener {
#Inject
EPartService partService;
public void openPart() {
MPart part = partService.createPart("org.eclipse.ui.browser.view");
part.setLabel("Browser");
partService.showPart(part, PartState.ACTIVATE);
}
}
Here you can find an example of how this works together with your Application.e4xmi.