/*******************************************************************************
* Copyright 2012 Geoscience Australia
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package au.gov.ga.earthsci.common.ui.util;
import java.awt.Component;
import java.awt.DefaultKeyboardFocusManager;
import java.awt.GraphicsEnvironment;
import java.awt.KeyboardFocusManager;
import java.awt.peer.KeyboardFocusManagerPeer;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.LinkedList;
/**
* Reflection bug-fix for <a
* href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6454631"
* >http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6454631</a>.
* <p/>
* How this fix works: The {@link #clearGlobalFocusOwner()} method ends up
* calling the static method markClearGlobalFocusOwner().
* markClearGlobalFocusOwner() checks the heavyweightRequests field for any
* objects. If it is not empty, the current focused window is retrieved from
* that. Otherwise it is retrieved from the peer. Retrieving it from the peer
* when the thread holds a lock on heavyweightRequests can sometimes cause a
* deadlock. So the fix is to ensure that the current focused window is never
* retrieved while holding a lock on heavyweightRequests, by passing the current
* focused window wrapped in a HeavyweightFocusRequest. This can be done by
* adding a new HeavyweightFocusRequest to the heavyweightRequests list, via
* reflection.
*
* @author Michael de Hoog (michael.dehoog@ga.gov.au)
*/
public class KeyboardFocusManagerFix extends DefaultKeyboardFocusManager
{
public static void initialize()
{
try
{
//if JRE is made by Sun/Oracle and is Java 1.6 or less, install the fix
//(the bug was fixed in Java 1.7)
String version = System.getProperty("java.version"); //$NON-NLS-1$
String vendor = System.getProperty("java.vendor"); //$NON-NLS-1$
vendor = vendor.toLowerCase();
version = version.replaceAll("\\D+", ""); //$NON-NLS-1$ //$NON-NLS-2$
String twoDigitVersion = version.substring(0, 2);
int intVersion = Integer.parseInt(twoDigitVersion);
if (intVersion <= 16 && (vendor.contains("sun") || vendor.contains("oracle"))) //$NON-NLS-1$ //$NON-NLS-2$
{
setCurrentKeyboardFocusManager(new KeyboardFocusManagerFix());
}
}
catch (Exception e)
{
//ignore
}
}
static
{
LinkedList<Object> heavyweightRequestsLocal = null;
try
{
Field heavyweightRequestsField = KeyboardFocusManager.class.getDeclaredField("heavyweightRequests"); //$NON-NLS-1$
heavyweightRequestsField.setAccessible(true);
@SuppressWarnings("unchecked")
LinkedList<Object> heavyweightRequestsUnchecked = (LinkedList<Object>) heavyweightRequestsField.get(null);
heavyweightRequestsLocal = heavyweightRequestsUnchecked;
}
catch (Exception e)
{
}
superHeavyweightRequests = heavyweightRequestsLocal;
}
public KeyboardFocusManagerFix()
{
try
{
Field peerField = KeyboardFocusManager.class.getDeclaredField("peer"); //$NON-NLS-1$
peerField.setAccessible(true);
superPeer = (KeyboardFocusManagerPeer) peerField.get(this);
}
catch (Exception e)
{
}
}
private transient KeyboardFocusManagerPeer superPeer;
private final static LinkedList<Object> superHeavyweightRequests;
@Override
public void clearGlobalFocusOwner()
{
if (!GraphicsEnvironment.isHeadless())
{
final Component nativeFocusedWindow = superPeer.getCurrentFocusedWindow();
synchronized (superHeavyweightRequests)
{
if (superHeavyweightRequests.isEmpty())
{
addHeavyweightFocusRequest(nativeFocusedWindow);
}
}
}
super.clearGlobalFocusOwner();
}
private void addHeavyweightFocusRequest(Component heavyweight)
{
Class<?>[] classes = KeyboardFocusManager.class.getDeclaredClasses();
Class<?> heavyweightFocusRequestClass = null;
for (Class<?> c : classes)
{
if (c.getSimpleName().equals("HeavyweightFocusRequest")) //$NON-NLS-1$
{
heavyweightFocusRequestClass = c;
break;
}
}
if (heavyweightFocusRequestClass != null)
{
try
{
Field heavyweightField = heavyweightFocusRequestClass.getDeclaredField("heavyweight"); //$NON-NLS-1$
heavyweightField.setAccessible(true);
Constructor<?> heavyweightFocusRequestDefaultConstructor =
heavyweightFocusRequestClass.getDeclaredConstructor();
heavyweightFocusRequestDefaultConstructor.setAccessible(true);
Object newHeavyweightFocusRequest = heavyweightFocusRequestDefaultConstructor.newInstance();
heavyweightField.set(newHeavyweightFocusRequest, heavyweight);
superHeavyweightRequests.add(newHeavyweightFocusRequest);
}
catch (Exception e)
{
}
}
}
}