Private APIs not usable in Java 9?

Stefan Fuchs snfuchs at gmx.de
Thu Apr 9 19:45:40 UTC 2015


Hi,

as promised here is the list of current private api usages of our application.
Any advice how to achieve the same with public apis is always welcome.


* We use com.sun.webkit.WebPage and com.sun.javafx.webkit.Accessor to implement our own frontend for the html editor.
   It seems to be unmaintained by oracle. Has anyone used an texteditor, where it is not possible to do a line break
   (https://javafx-jira.kenai.com/browse/RT-38412)? Bug was filed August 2014 and was deferred from 8u40 to 9.
   The bug can only be tracked down by oracle, because the bug is probably in the plugin code, which is still closed source.
   Well we have a semi working workaround involving private apis...
   Whatever we currently implement a new text editor based on TextFlow, because the html editor is simply too buggy.
   So for us this will no longer an issue with Java 9.

* com.sun.javafx.scene.control.skin.ComboBoxListViewSkin is used to scroll to entry matching typed key in a ComboBoxListView
   There is a feature request from 2011 for this: https://javafx-jira.kenai.com/browse/RT-18064.
   Perhaps its enough to expose scrollTo. That is https://javafx-jira.kenai.com/browse/RT-34661.

    fontFamilyComboBox.setOnKeyPressed(new EventHandler<KeyEvent>() {
             	
		@Override
                 	public void handle(KeyEvent event) {
             		
             			if (fontFamilyComboBox.isShowing()) {

     	        			@SuppressWarnings("rawtypes")
     	        			ListView cbListView = ((ComboBoxListViewSkin) fontFamilyComboBox.getSkin()).getListView();
     	        		
					String typed = event.getCode().toString().toLowerCase();
     	            			ObservableList<String> fontList = fontFamilyComboBox.getItems();
     	            			for (int i = 0; i < fontList.size(); i++) {
     	            				String font = fontList.get(i).toLowerCase();
     	            				if (font.startsWith(typed)) {
     	            					fontFamilyComboBox.getSelectionModel().clearAndSelect(i);
     	            					cbListView.scrollTo(i);
     	            					break;
     	            				}
     	            			}		
             			}
		}
	});

* We maximize our application window to full screen on startup with this code
This is a workaround for https://javafx-jira.kenai.com/browse/RT-32422

import com.sun.glass.ui.Window;
import com.sun.javafx.application.PlatformImpl;

             if (!isEmbedded && Window.getFocusedWindow() != null) {
                 Logger.getLogger(getClass()).debug(Settings.getInstance().getBoolProperty("mainStageMaximized", true));
                 try {
                     Window.getFocusedWindow().maximize(Settings.getInstance().getBoolProperty("mainStageMaximized", true));
                 } catch (Exception e) {
                     // java.lang.IllegalStateException: The window has already been closed on linux
                 }
                 Logger.getLogger(getClass()).info("maximized focused window " + Window.getFocusedWindow().getTitle());
                 
          }

* ComboBoxes, we have the requirement to automatically open comboboxes on mouseover. The combobox should not be closed on mouseclick:

  Expose the value of isHideOnClickEnabled in our ExtendedComboBox control.

	@Override
	protected Skin<?> createDefaultSkin() {
		Skin<?> s = new ComboBoxListViewSkin<T>(this){
         	
			@Override
			protected boolean isHideOnClickEnabled() {
				return getIsHideOnClickEnabled();
			}
			
			@Override
			protected PopupControl getPopup() {
				PopupControl p = super.getPopup();
				p.autoHideProperty().bind(autoHideProperty);
				return p;
			}
         		};
   		return s;
     	}


* com.sun.glass.ui.Robot is used to work around a bug, that the Browser window does not get focus after the SWT-Filedialog is closed on Mac
-> We would not have to use the SWT-Filedialog at all and could use the JavaFX-Filedialog, ifhttps://javafx-jira.kenai.com/browse/RT-38809  was fixed.

Browser Mode: activate browser after swt-filedialog has been closed
             if(EditorFX.getInstance().getHostServices().getWebContext()!=null) {
                 LOG.debug("disposeAll -> create Robot");
                 Robot robot = com.sun.glass.ui.Application.GetApplication().createRobot();
                 if(robot!=null) {
                     robot.mousePress(1);
                     robot.mouseRelease(1);
                     robot.destroy();
                     LOG.debug("disposeAll -> Robot destroyed");
                 }
             }

* com.sun.glass.ui.Robot is also used to get screen coordinates for checks, if the mouse is over a node or not.
  We found that mouseEntered and mouseExited events do not work reliable. E.g.: mouseExited Events not triggered, mouseEntered are triggered before mouseExited on previous node)

* Gather various information about the used hardware in case of an unhandled exception.
   In case of an unhandled exception we gather various information about the affected computer and ask the user to upload it to us for further analysis.
  Our dialog is similar to what Firefox does:https://support.mozilla.org/en-US/kb/mozillacrashreporter
  These are some of our hacks to get information about the current hardware used:

	public static long getTotalPhysicalMemorySize() {
		com.sun.management.OperatingSystemMXBean os = (com.sun.management.OperatingSystemMXBean)
			     java.lang.management.ManagementFactory.getOperatingSystemMXBean();
		return os.getTotalPhysicalMemorySize();
	}
	
	public static long getFreePhysicalMemorySize() {
		com.sun.management.OperatingSystemMXBean os = (com.sun.management.OperatingSystemMXBean)
			     java.lang.management.ManagementFactory.getOperatingSystemMXBean();
		return os.getFreePhysicalMemorySize();



getWindowsGraphicCardInfo retrieves the same information, which would have been logged by prism.verbose=true

public class WindowsHardwareInfo {

	public static String getWindowsGraphicCardInfo() {
		
		String info = "";
		
		try {
			// ResourceFactory resourceFactory = D3DPipeline.getDefaultResourceFactory()
			Class<?> d3DPipelineClass = (Class<?>) Class.forName("com.sun.prism.d3d.D3DPipeline");
			Method getDefaultResourceFactoryMethod = d3DPipelineClass.getMethod("getDefaultResourceFactory");
			ResourceFactory resourceFactory = (ResourceFactory) getDefaultResourceFactoryMethod.invoke(null);
			
			info += "Maximum texture size: " + resourceFactory.getMaximumTextureSize() + "\n" ;
			
			// int adapterCount = D3DPipeline.nGetAdapterCount()
			Method nGetAdapterCountMethod =  d3DPipelineClass.getDeclaredMethod("nGetAdapterCount");
			nGetAdapterCountMethod.setAccessible(true);
			int adapterCount = (Integer) nGetAdapterCountMethod.invoke(null);
			
			Class<?> d3DDriverInformationClass = (Class<?>) Class.forName("com.sun.prism.d3d.D3DDriverInformation");
			Method nGetDriverInformationMethod = d3DPipelineClass.getDeclaredMethod("nGetDriverInformation", int.class, d3DDriverInformationClass );
			nGetDriverInformationMethod.setAccessible(true);

			Constructor<?> constructor = d3DDriverInformationClass.getDeclaredConstructor();
			constructor.setAccessible(true);

			for (int adapter = 0, n = adapterCount; adapter != n; ++adapter) {
				//D3DPipeline.nGetDriverInformation(adapter, new D3DDriverInformation());
				Object di = nGetDriverInformationMethod.invoke(null, adapter, constructor.newInstance());
			
			    if (di != null) {
			    	info += "Graphics adapter #" + adapter + "\n";
			    	info += "OS Information:\n";
			    	info += "\t" + callGetter(di, d3DDriverInformationClass, "getOsVersion") + " build " + getField(di, d3DDriverInformationClass, "osBuildNumber") + "\n";
			    	info += "D3D Driver Information:\n";
			    	info += "\t" + getField(di, d3DDriverInformationClass, "deviceDescription")  + "\n";
			    	info += "\t" + getField(di, d3DDriverInformationClass, "deviceName") + "\n";
			    	info += "\tDriver " + getField(di, d3DDriverInformationClass, "driverName") + ", version " + callGetter(di, d3DDriverInformationClass, "getDriverVersion") + "\n";
			    	info += "\tPixel Shader version " + getField(di, d3DDriverInformationClass, "psVersionMajor") + "." + getField(di, d3DDriverInformationClass, "psVersionMinor") + "\n";
			    	info += "\tDevice : " + callGetter(di, d3DDriverInformationClass, "getDeviceID") + "\n";
//		            System.out.println("\tMax Multisamples supported: " + di.maxSamples);
			        Object warning = getField(di, d3DDriverInformationClass, "warningMessage");
			        if (warning != null) {
			        	info += "\t *** " + warning + "\n";
			        }
			    }
			}
		} catch (Exception e) {
			info += "Unable to retrieve hardware info: " + e.getMessage();
		}
			
		return info;
	}
	
	private static Object callGetter(Object object, Class<?> objectClass, String methodName) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Method method = objectClass.getDeclaredMethod(methodName);
		method.setAccessible(true);
		return method.invoke(object);
	}
	
	private static Object getField(Object object, Class<?> objectClass, String fieldName) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		Field field = objectClass.getDeclaredField(fieldName);
		field.setAccessible(true);
		return field.get(object);
	}


For OutOfMemory exceptions, we create a heapdump file like this:

     public static void dumpHeap(String fileName, boolean live) throws Exception {
         // initialize hotspot diagnostic MBean
     	Object hotspotMBean = getHotspotMBean();
         Class<?> clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
         Method m = clazz.getMethod("dumpHeap", String.class, boolean.class);
         m.invoke( hotspotMBean , fileName, live);
     }

     // get the hotspot diagnostic MBean from the
     // platform MBean server
     private static Object getHotspotMBean() throws ClassNotFoundException, IOException {
         Class<?> clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
         MBeanServer server = ManagementFactory.getPlatformMBeanServer();
         return ManagementFactory.newPlatformMXBeanProxy(server, HOTSPOT_BEAN_NAME, clazz);
     }


These are the current usages of private apis in our application.
However we include various other libraries, which I have not checked yet. Some of them are no longer maintained.

So obviously for most of the problems there are open jiras, many of them years old.


- Stefan

> I' ll try to compile a list of the private apis we currently use in 
> our application and why.
>
> Looking forward to using only public apis in java 9 then :-)
>
> - Stefan
>
>>> On Apr 8, 2015, at 1:52 PM, Robert Krüger <krueger at lesspain.de> wrote:
>>>   our only workaround is to use private API
>> For the benefit of the devs on the list, could you please point out 
>> what private APIs you currently need to use?  That way we can make 
>> sure proper JIRAs are filed and we can connect those to actual 
>> real-world problems.
>



More information about the openjfx-dev mailing list