No Business Naming Things

Where sassy women wear jaunty hats.

No Business Naming Things header image 4

Christmas Cards

December 18th, 2007 · No Comments

I apologize to anyone waiting for a Christmas card.  We have them stamped and ready to go - we’re just missing the labels.  I doubt they’ll make it on time for Christmas time but I guess that’s what we get for being slackers!



Tags:

Eclipse 3.3 Startup Changes Take Two

December 18th, 2007 · 1 Comment

There is another variant of the workbench startup problem I talked about previously that is outlined in bug 195664. In a nutshell, the problem is this.

In your editor (or view) part init method you need to pop up a progress dialog for some reason. When that kicks off, it uses a sync exec to do some work within ModalContext. After the workbench has started, this isn’t an issue, but if you hit this on startup there’ll be issues. The workbench is in a state where non-startup (a)sync execs are batched up for execution at a later time, after initialization is complete. Since we’re invoking one of these guys from the UI thread and waiting for it to complete we’re going to deadlock. Boourns.

So, what to do? Well, to start with, you probably don’t want to do any operation long enough to need a progress bar in the init method. Instead of performing the operation at that time instead defer the operation until after the editor is initialized. To do this you can use UI jobs. For instance, imagine an editor that prompts for a log-in before showing contents and then shows progress while the log-in is verified. The code could look something like the following:

package interactiveEditor; // snip imports public class InteractiveTextEditorWithLogin extends TextEditor {         private StackLayout layout = new StackLayout();         private Composite composite;         public void init(final IEditorSite site, IEditorInput input)                         throws PartInitException {                 setSite(site);                 setInput(input);                 Job loginJob = new LoginJob("login", site);                 loginJob.schedule();         }         public void createPartControl(Composite parent) {                 composite = new Composite(parent, SWT.NONE);                 composite.setLayout(layout);                 Label label = new Label(composite, SWT.NONE);                 label.setText("Please log in.");                 layout.topControl = label;         }         protected void createRealContents() {                 Composite realComposite = new Composite(composite, SWT.NONE);                 realComposite.setLayout(new FillLayout());                 super.createPartControl(realComposite);                 layout.topControl = realComposite;                 composite.layout(true);         }         private final class LoginJob extends UIJob {                 private final IEditorSite site;                 private LoginJob(String name, IEditorSite site) {                         super(name);                         this.site = site;                 }                 public IStatus runInUIThread(IProgressMonitor monitor) {                         Dialog dialog = new LoginDialog(site.getWorkbenchWindow()                                         .getShell(), site);                         dialog.open();                         return Status.OK_STATUS;                 }         }         private final class LoginDialog extends Dialog {                 private final class LoginRunnable implements IRunnableWithProgress {                         public void run(IProgressMonitor monitor)                         throws InvocationTargetException,                         InterruptedException {                                 monitor.beginTask("Logging in", 10);                                 for (int i = 0; i < 10; i++) {                                         monitor.worked(1);                                         Thread.sleep(500);                                 }                         }                 }                 private final IEditorSite site;                 private LoginDialog(Shell parentShell, IEditorSite site) {                         super(parentShell);                         this.site = site;                 }                 protected Control createDialogArea(Composite parent) {                         Composite composite = (Composite) super.createDialogArea(parent);                         Label nameLabel = new Label(composite, SWT.NONE);                         nameLabel.setText("Name:");                         new Text(composite, SWT.SINGLE);                         Label passwordLabel = new Label(composite, SWT.NONE);                         passwordLabel.setText("Password:");                         new Text(composite, SWT.PASSWORD);                         return composite;                 }                 protected void createButtonsForButtonBar(Composite parent) {                         createButton(parent, 1, "Login", true);                 }                 protected void buttonPressed(int buttonId) {                         close();                 }                 public boolean close() {                         boolean code = super.close();                         ProgressMonitorDialog pDialog = new ProgressMonitorDialog(site                                         .getWorkbenchWindow().getShell());                         try {                                 pDialog.run(false, false, new LoginRunnable());                         } catch (InvocationTargetException e) {                                 e.printStackTrace();                         } catch (InterruptedException e) {                                 e.printStackTrace();                         }                         createRealContents();                         return code;                 }         } }

Instead of opening the login dialog (and subsequent progress dialog) directly in init we instead spawn a job that will open the the dialogs when executed. The initial contents of the editor is a simple label and only after the heavy lifting has been performed by the job are the real controls for the editor created and populated. This technique will work both when the editor is opened in a running Eclipse instance and when it is restored on startup in subsequent Eclipse instances - the workbench is smart enough not to run UIJobs until everything is settled after a startup.

So, this code will work but it still isn’t necessarily a good idea. Opening a dialog as a consequence of opening an editor is bad form. What would happen if you had two of these editors visible on startup? You’d have two dialogs opened with no idea which editor they belonged to. Instead, you might want to try the following code instead:

package interactiveEditor; // snip imports public class InteractiveTextEditorWithLoginNoJobs extends TextEditor {         private final class LoginRunnable implements IRunnableWithProgress {                 public void run(IProgressMonitor monitor)                                 throws InvocationTargetException, InterruptedException {                         monitor.beginTask("Logging in", 5);                         for (int i = 0; i < 5; i++) {                                 monitor.worked(1);                                 Thread.sleep(250);                         }                 }         }         private StackLayout layout = new StackLayout();         private Composite composite;         @Override         public void init(final IEditorSite site, IEditorInput input)                         throws PartInitException {                 setSite(site);                 setInput(input);         }         @Override         public void createPartControl(Composite parent) {                 composite = new Composite(parent, SWT.NONE);                 composite.setLayout(layout);                 Composite loginComposite = new Composite(composite, SWT.NONE);                 loginComposite.setLayout(new GridLayout(2, false));                 Label label = new Label(loginComposite, SWT.NONE);                 label.setText("Please log in.");                 label.setLayoutData(new GridData(GridData.BEGINNING,                                 GridData.BEGINNING, true, false, 2, 1));                 Label nameLabel = new Label(loginComposite, SWT.NONE);                 nameLabel.setText("Name:");                 nameLabel.setLayoutData(new GridData(GridData.BEGINNING,                                 GridData.BEGINNING, true, false, 1, 1));                 Text nameText = new Text(loginComposite, SWT.SINGLE);                 nameText.setLayoutData(new GridData(GridData.BEGINNING,                                 GridData.BEGINNING, true, false, 1, 1));                 Label passwordLabel = new Label(loginComposite, SWT.NONE);                 passwordLabel.setText("Password:");                 passwordLabel.setLayoutData(new GridData(GridData.BEGINNING,                                 GridData.BEGINNING, true, false, 1, 1));                 Text passwordText = new Text(loginComposite, SWT.PASSWORD);                 passwordText.setLayoutData(new GridData(GridData.BEGINNING,                                 GridData.BEGINNING, true, false, 1, 1));                 Button loginButton = new Button(loginComposite, SWT.PUSH);                 loginButton.setText("Login");                 loginButton.setLayoutData(new GridData(GridData.BEGINNING,                                 GridData.BEGINNING, true, false, 2, 1));                 loginButton.addSelectionListener(new SelectionAdapter() {                         public void widgetSelected(SelectionEvent e) {                                 ProgressMonitorDialog pDialog = new ProgressMonitorDialog(                                                 getSite().getWorkbenchWindow().getShell());                                 try {                                         pDialog.run(false, false, new LoginRunnable());                                 } catch (InvocationTargetException ex) {                                         ex.printStackTrace();                                 } catch (InterruptedException ex) {                                         ex.printStackTrace();                                 }                                 createRealContents();                         }                 });                 layout.topControl = loginComposite;         }         protected void createRealContents() {                 Composite realComposite = new Composite(composite, SWT.NONE);                 realComposite.setLayout(new FillLayout());                 super.createPartControl(realComposite);                 layout.topControl = realComposite;                 composite.layout(true);         } }

This code will embed log in controls into the editor instead of opening a dialog to display them. This is a much less offensive solution, particularly in the restoration case. No one wants a dialog in the eye first thing in the morning after booting Eclipse!



Tags: