Swing app temporarily "freezing" using Docking Frames

Hi all

We have developped a Swing application, running on Java 6 and used Docking Frames (upgraded to last version 1.1.1).
We encounter the following problem.

After 2 to 4 hours using the application, this seems to freeze temporarily.
The text fields or comboboxes are blocked (not focusable ?).
The user can type text but the application will consider only a random part of the text, some letters or some numbers. He won’t be able to select an item in a combobox, the combobox will close itself while the user tries to select an item.
If the user waits 3 to 5 seconds, the application will unfreeze.
But the more the user insists typing text or selecting an item, the more the freezing will stand, and the freezing will happen more and more often.
It seems like a process / thread is not terminated…

The memory usage has been optimized and the GC behaviour is normal (no freezing due to major GC).
There is no database sollicitation when the problem happens.

It is important to notice that, using MDIFrames (default swing docking), the freezing does not happen.
But we really would like to keep using Docking Frames !
Has anyone already encountered a similar problem ? What can we do ?
Are there any identified bottlenecks using Docking Frames ?

Thanks for your help

Hi all

nobody can help me ? / niemand kann mir helfen ?

There is always a chance of memory leaks in DockingFrames, but there are no known leaks. You are the first one describing these issues, so… does your client anything special?

I currently don’t have any good idea that could help you, all I can do is write myself an automatic test and see if I get the same issues. But that has to wait until the weekend.

Thanks for answering

Asking the users to run the application with standard MDIFrames docking, we encountered a quite different problem : the application progressively slows down after 4/5 hours use. (There are still no suspect GC behaviour.)

In fact, i’m wondering if the problem is not the same…
The progressive slowdown of the application shows itself by 2 different ways :

  • simple slowdown using MDIFrames
  • temporarily freeze using Docking Frames
    Well… it’s hard to analyze, i’m not developer but project manager…
    Maybe some “swing good/best practices” haven’t been respected.
    Maybe we generated some threads bottlenecks.
    I think we should try to profile our application but i can’t find any help / tutorial about profiling a swing application.
    I tried “YourKit Java Profiler 11.0.9” : it seems to be a good profiling software. But it gives so many informations… I’m quite lost
    Have you ever profiled a swing app ? Do you know which are the most important things to consider ? Especially about threads and bottlenecks ?

thanks for your help

Not knowing your software (this size, complexity, involved skills, number of users) I can only make guesses. It could be something very simple like a listener that has been added multiple times to some button (and the same work is done a few 1000 times if that button is pressed).

I have not much experience with profiling, but comparing snapshots could be a good start. Make a snapshot all 10 minutes or so. Does the number of some type of objects increase significantly? If yes: why? And what kind of objects?

I think yourkit allows to analyze which methods are called? Let it run just for performing one or two actions. Does the number of called methods chance significantly over time? If yes: which methods are called more often?

Thanks for answering

The more I consider our problem, the more I think Docking Frames is not involved.
We try to analyze threading, swingworker usage, EDT / AWT swing principles, and so on.

If we find an explanation and a solution, i’ll post it here. It could help other ones.

Hi all

I have found a way to reproduce systematically our problem.

Let’s consider a window containing an identification criteria. The identification criteria works fine.
Suppose I select a button or a tab label in the same window : the focus is now set on the selected item.
Then, I open another window, positionning as a bottom tab (docking frames behaviour, upgraded to last version 1.1.1).
When I go back to the first window and immediatly try to set the focus on the identification criteria to type a new identifier, I’m unable to type the identifier. As described earlier, only random caracters are accepted. If I try to open the combobox, it closes itself immediatly.
I get control again on the identification criteria after 4 to 5 seconds at least.

Otherwise, if I wait 4 to 5 seconds after going back to the first window, I encounter no problem.

The joined PDF file contains screenshots illustrating the problem.

Is it something like a « focus stealing » ? « focus disabling » ? Or is it due to the repaint duration ?
What about using « setPreventFocusStealing » ?

Did you encounter such a behaviour ? Have you got any idea ? With my description, can you reproduce that behaviour ?

I don’t encounter this problem using swing default MDIFrames.

Thanks for helping

Just curious: When you click into the text field to enter the new ID number: Does ist immediately “accept” the cursor? Is the cursor blinking? Are there any FocusListeners registered to the involved components? (BTW: I assume that a small compilable example where the error can be reporoduced might help to find out what’s wrong there, but see that it might be hard to isolate such a sample…)

I tried to create an application reproducing the bug after your description (see code below), but it did not freeze. Do you see any Component or piece of code missing to perform the steps you described? By the way, thanks for the screenshots.

If you focus a Dockable, the framework tries to focus a child Component of that Dockable by force. But the algorithm does not try more than 20 times (with a pause of 10 milliseconds between each attempt) to set the focus. As a result, after 200 milliseconds the framework stops playing around with focus - this does not explain the 3-5 seconds of freezing you see.

About “setPreventFocusStealing”: you mean ScreenDockWindow.setPreventFocusStealing? That only affects floating (externalized) dockables. It is called when the owner window of a ScreenDockStation changes, in this case for a short amount of time all windows are made invisible and recreated. Making the windows visible again causes Swing to automatically transfer focus, unless prevented by calling Window.setFocusableWindowState. But applications should not call this method.

Other than that I can only ask the same questions Marco already did.


import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.DefaultSingleCDockable;

public class FreezeIssueTest {
	public static void main( String[] args ){
		JFrame frame = new JFrame( "Freeze" );
		CControl control = new CControl( frame );
		
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.setBounds( 20, 20, 400, 400 );
		frame.add( control.getContentArea() );
		
		final PersonDockable person = new PersonDockable();
		final SearchDockable search = new SearchDockable( person );
		
		control.addDockable( person );
		control.addDockable( search );
		
		search.setVisible( true );
		
		JPanel topPanel = new JPanel( new GridLayout( 1, 2 ) );
		JButton open = new JButton( "Open results" );
		
		topPanel.add( open );
		topPanel.add( new JButton( "No action" ) );
		
		frame.add( topPanel, BorderLayout.NORTH );
		open.addActionListener( new ActionListener(){
			@Override
			public void actionPerformed( ActionEvent e ){
				search.openResult();
			}
		});
		
		frame.setVisible( true );
	}
	
	private static class SearchDockable extends DefaultSingleCDockable{
		private PersonDockable searchResult;
		
		public SearchDockable( PersonDockable result ){
			super( "search" );
			setTitleText( "Traitement des évènements" );
			this.searchResult = result;
			
			JTextField idField = new JTextField();
			JButton okButton = new JButton( "Ok" );
			
			setLayout( new GridBagLayout() );
			Insets insets = new Insets( 1, 1, 1, 1 );
			add( new JLabel("Id"), new GridBagConstraints( 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insets, 0, 0 ) );
			add( idField, new GridBagConstraints( 1, 0, 1, 1, 5.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insets, 0, 0 ) );
			
			add( okButton, new GridBagConstraints( 0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, insets, 0, 0 ));
			
			okButton.addActionListener( new ActionListener(){
				@Override
				public void actionPerformed( ActionEvent e ){
					openResult();
				}
			});
		}
		
		public void openResult(){
			searchResult.setLocation( getBaseLocation().aside() );
			searchResult.setVisible( true );
		}
	}
	
	private static class PersonDockable extends DefaultSingleCDockable{
		public PersonDockable(){
			super( "test person" );
			setTitleText( "Personne Morale" );
			
			setCloseable( true );
			
			JTabbedPane content = new JTabbedPane();
			add( content, BorderLayout.CENTER );
			
			JPanel general = new JPanel( new GridLayout( 2, 2 ));
			general.add( new JLabel( "No" ) );
			general.add( new JTextField() );
			
			general.add( new JLabel( "Name" ) );
			general.add( new JTextField() );
			
			content.addTab( "Tab 1", general );
		}
	}
}

Thanks to both of you Marco and Beni

**>>>When you click into the text field to enter the new ID number: Does it immediately “accept” the cursor? Is the cursor blinking?
**

When I click on the criteria text field (or when I try to open the combobox to select a criteria on an another type of window), the cursor sets in the field but quite immediatly goes out.
If I try as soon as possible to type some characters, I may achieve to type some, but the cursor goes out randomly while I’m typing.

>>>Are there any FocusListeners registered to the involved components?

Yes there are. We use many FocusListeners in the application. Is there any issue using them ?

**>>>If you focus a Dockable, the framework tries to focus a child Component of that Dockable by force. But the algorithm does not try more than 20 times (with a pause of 10 milliseconds between each attempt) to set the focus. As a result, after 200 milliseconds the framework stops playing around with focus - this does not explain the 3-5 seconds of freezing you see.
**

This seems to me very interesting.
Analyzing the code, we found that the « panel.SetLocked(false) » instructions are invoked too early. The processes are not terminated.
As a result, the users are able to change the identifying criteria or interact anyway with the application.
First of all, we must invoke the « panel.SetLocked(false) » instructions at the right time, to avoid unexpected users interactions. The users will perhaps wait longer… but without unpredictable application behaviour.
However, the behaviour you describe let me think that it explains our problem. The user interaction could make the « native focus behaviour process » last more than 200 milliseconds.
What do you think about that ?
Is there a way to stop this behaviour of the framework ? I’m curious about testing the application this way…

Thanks again !

One very quick way to disable the entire focus mechanism of the framework is this code:

control.getController().getFocusController().freezeFocus();```

Or - to make the test a little bit more interesting - you can replace the FocusController. FocusController is an interface, but for the test just make a subclass for DefaultFocusController. It's a little bit tricky to install the FocusController, as you have to replace several factories:

[Edit: I wrote this code with the newest version of the framework, maybe you'll need to upgrade first - or resolve some compiler errors]

```package docktest;

import java.awt.Component;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

import bibliothek.gui.DockController;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.intern.CDockController;
import bibliothek.gui.dock.common.intern.EfficientControlFactory;
import bibliothek.gui.dock.control.ControllerSetupCollection;
import bibliothek.gui.dock.control.DefaultDockControllerFactory;
import bibliothek.gui.dock.control.DefaultFocusController;
import bibliothek.gui.dock.control.focus.DefaultFocusStrategy;
import bibliothek.gui.dock.control.focus.FocusController;
import bibliothek.gui.dock.control.focus.FocusRequest;
import bibliothek.gui.dock.control.focus.RepeatingFocusRequest;
import bibliothek.gui.dock.event.ControllerSetupListener;

public class FocusTesting {
	public static void main( String[] args ){
		JFrame frame = new JFrame();
		frame.setBounds( 20, 20, 400, 400 );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		
		CControl control = new CControl( frame, new CustomCControlFactory() );
		// CControl control = new CControl( frame );
		frame.add( control.getContentArea() );
		
		CGrid grid = new CGrid( control );
		grid.add( 0, 0, 1, 1, newDockable( "Aaaa" ) );
		grid.add( 1, 0, 1, 1, newDockable( "Bbbb" ) );
		grid.add( 1, 0, 1, 1, newDockable( "Cccc" ) );
		control.getContentArea().deploy( grid );
		
		frame.setVisible( true );
	}
	
	private static DefaultSingleCDockable newDockable( String title ){
		DefaultSingleCDockable dockable = new DefaultSingleCDockable( title, title );
		dockable.add( new JScrollPane( new JTextArea() ) );
		return dockable;
	}
	
	private static class CustomCControlFactory extends EfficientControlFactory{
		@Override
		public DockController createController( CControl owner ){
			return new CDockController( owner, new CustomDockControllFactory() );
		}
	}

	private static class CustomDockControllFactory extends DefaultDockControllerFactory{
		@Override
		public FocusController createFocusController( DockController controller, ControllerSetupCollection setup ){
	    	final CustomFocusController focus = new CustomFocusController( controller );
	    	
	    	setup.add( new ControllerSetupListener(){
				public void done( DockController controller ){
					focus.setStrategy( new DefaultFocusStrategy( controller ) );
				}
			});
		    
	    	return focus;
		}
	}
	
	/*
	 * This simplified FocusController does not repeat focus requests (and has some issues with being
	 * too simple - but it is only for testing).
	 */
	private static class CustomFocusController extends DefaultFocusController{
		public CustomFocusController( DockController controller ){
			super( controller );
		}
		
		@Override
		protected void execute( FocusRequest request, Dockable dockable, Component component ){
			if( component != null ){
				// request the focus exactly one time
				RepeatingFocusRequest simplified = new RepeatingFocusRequest( request.getSource(), component, 1, 1, request.isHardRequest() );
				super.execute( simplified, dockable, component );
			}
		}
	}
}

Hi Beni

Thank you very much !!! you’re great !!!
We implemented the solution you suggested and it worked totally fine.

3 months searching, analyzing the bug, optimizing the application… and at last your help and your solution !
50 users and a project team relieved !

Once more : thank you !!!

Wow, and all of this because of me not understanding how focus in Swing works… well if you have new strange issues, you now know in which framework to search :wink: