From michael.heinrichs at oracle.com Mon Jan 2 06:32:01 2012 From: michael.heinrichs at oracle.com (Michael Heinrichs) Date: Mon, 2 Jan 2012 15:32:01 +0100 Subject: JavaBeanPropertyAdapter Reloaded Message-ID: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> Hi everybody, I took the feedback for the first version of the Java Bean adapter and created a new design with the feedback incorporated. The general idea is to take whatever is available and use it. For example, if the Java Bean property supports change listeners, this will be used to synchronize changes from the Java Bean property to the JavaFX property. If there is no support for change listeners, you can still create an adapter, but you have to do the synchronization yourself or disallow changing the Java Bean property. I put everything in separated classes now and added a new package that contains the new classes and interfaces: javafx.beans.property.adapter. There are two new interfaces that define the common functionality of all JavaBeanProperty / ReadOnlyJavaBeanProperty instances: public interface ReadOnlyJavaBeanProperty extends ReadOnlyProperty { void fireValueChangedEvent(); void dispose(); } public interface JavaBeanProperty extends ReadOnlyJavaBeanProperty, Property {} The method fireValueChangedEvent() can be called to notify the adapter that the Java Bean property changed in case it does not support change listeners. The implementation uses WeakReferences and some automatic cleanup, if there are no references to the property left. But if you want more control, you can call dispose() on an adapter to remove all hooks to the bean. Then we have a bunch of implementations for all types of Java Bean adapters, e.g.: public final class ReadOnlyJavaBeanBooleanProperty extends ReadOnlyBooleanProperty implements ReadOnlyJavaBeanProperty {...} public final class ReadOnlyJavaBeanObjectProperty extends ReadOnlyObjectProperty implements ReadOnlyJavaBeanProperty {...} public final class JavaBeanBooleanProperty extends BooleanProperty implements JavaBeanProperty {...} public final class JavaBeanObjectProperty extends ObjectProperty implements JavaBeanProperty {...} And finally the really interesting part, the builders. There are two possibilities to use them. If you need to create an adapter for the same property of a collection of beans, you should first create the builder and then call createXYZProperty() for each bean. If you need to create just one adapter, there are static methods to do everything with a single call. public final class ReadOnlyJavaBeanPropertyBuilder { public static ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} public static ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} // same for all other types public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName) throws NoSuchMethodException {...} public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter) {...} public ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean) {...} public ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean) {...} // same for all other types } public final class JavaBeanPropertyBuilder { public static JavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} public static JavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} // same for all other types public JavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} public JavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName, String setterName) throws NoSuchMethodException {...} public JavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter, Method setter) {...} public JavaBeanBooleanProperty createBooleanProperty(Object bean) {...} public JavaBeanObjectProperty createObjectProperty(Object bean) {...} // same for all other types } What do you think? Thanks, Michael From hang.vo at oracle.com Mon Jan 2 11:22:15 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 02 Jan 2012 19:22:15 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-18453 menubar steas focus on click making it impossible to copy paste selected text in Message-ID: <20120102192215.D69A04785B@hg.openjdk.java.net> Changeset: 58782d435401 Author: Paru Somashekar Date: 2012-01-02 11:23 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/58782d435401 fix RT-18453 menubar steas focus on click making it impossible to copy paste selected text in TextField. Also included Leif's patch for MenuButton and mnemonics. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuBarSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuButtonSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css From jonathan.giles at oracle.com Mon Jan 2 17:19:51 2012 From: jonathan.giles at oracle.com (Jonathan Giles) Date: Tue, 03 Jan 2012 11:19:51 +1000 Subject: JavaBeanPropertyAdapter Reloaded In-Reply-To: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> References: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> Message-ID: <4F0257B7.8070600@oracle.com> Michael, I'm interested to learn how you foresee these being used in the UI controls (mainly ListView and TableView). In particular, the TableView control (but any thoughts you have on the simpler ListView control would be appreciated too). To give a brief overview, TableView has an items ObservableList. Each TableColumn has a cellValueFactory (not to be confused with cellFactory) which is responsible for retrieving the data for a given column. The most common implementation of the cellValueFactory is javafx.scene.control.cell.PropertyValueFactory. This class uses the old PropertyReference API internally to reflectively get the Property if it is available (and this is then bound to so that changes are automatically reflected visually). If there is no *property() method, we try to call the get/is*() to get the value, and wrap this in a ReadOnlyObjectWrapper. This means that the value will be forever static (unless the developer rebuilds the TableView). Can I use the JavaBeanPropertyAdapter to add more functionality here (in particular, observability of JavaBean properties)? If so, is it cheap enough to have many of these bindings (I only create bindings for the visible cells, not for all of them). -- Jonathan On 3/01/2012 12:32 a.m., Michael Heinrichs wrote: > Hi everybody, > > I took the feedback for the first version of the Java Bean adapter and created a new design with the feedback incorporated. The general idea is to take whatever is available and use it. For example, if the Java Bean property supports change listeners, this will be used to synchronize changes from the Java Bean property to the JavaFX property. If there is no support for change listeners, you can still create an adapter, but you have to do the synchronization yourself or disallow changing the Java Bean property. > > I put everything in separated classes now and added a new package that contains the new classes and interfaces: javafx.beans.property.adapter. > > > > There are two new interfaces that define the common functionality of all JavaBeanProperty / ReadOnlyJavaBeanProperty instances: > > public interface ReadOnlyJavaBeanProperty extends ReadOnlyProperty { > void fireValueChangedEvent(); > void dispose(); > } > > public interface JavaBeanProperty extends ReadOnlyJavaBeanProperty, Property {} > > The method fireValueChangedEvent() can be called to notify the adapter that the Java Bean property changed in case it does not support change listeners. The implementation uses WeakReferences and some automatic cleanup, if there are no references to the property left. But if you want more control, you can call dispose() on an adapter to remove all hooks to the bean. > > > > Then we have a bunch of implementations for all types of Java Bean adapters, e.g.: > > public final class ReadOnlyJavaBeanBooleanProperty extends ReadOnlyBooleanProperty implements ReadOnlyJavaBeanProperty {...} > > public final class ReadOnlyJavaBeanObjectProperty extends ReadOnlyObjectProperty implements ReadOnlyJavaBeanProperty {...} > > public final class JavaBeanBooleanProperty extends BooleanProperty implements JavaBeanProperty {...} > > public final class JavaBeanObjectProperty extends ObjectProperty implements JavaBeanProperty {...} > > > > And finally the really interesting part, the builders. There are two possibilities to use them. If you need to create an adapter for the same property of a collection of beans, you should first create the builder and then call createXYZProperty() for each bean. If you need to create just one adapter, there are static methods to do everything with a single call. > > public final class ReadOnlyJavaBeanPropertyBuilder { > public static ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > public static ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > // same for all other types > > public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} > public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName) throws NoSuchMethodException {...} > public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter) {...} > > public ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean) {...} > public ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean) {...} > // same for all other types > } > > public final class JavaBeanPropertyBuilder { > public static JavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > public static JavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > // same for all other types > > public JavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} > public JavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName, String setterName) throws NoSuchMethodException {...} > public JavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter, Method setter) {...} > > public JavaBeanBooleanProperty createBooleanProperty(Object bean) {...} > public JavaBeanObjectProperty createObjectProperty(Object bean) {...} > // same for all other types > } > > What do you think? > > Thanks, > Michael From michael.heinrichs at oracle.com Tue Jan 3 01:16:08 2012 From: michael.heinrichs at oracle.com (Michael Heinrichs) Date: Tue, 3 Jan 2012 10:16:08 +0100 Subject: JavaBeanPropertyAdapter Reloaded In-Reply-To: <4F0257B7.8070600@oracle.com> References: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> <4F0257B7.8070600@oracle.com> Message-ID: Hi Jonathan, I have not explicitly tested the prototype with ListView or TableView, but I think it can be integrated easily. The adapters for Java Beans will make the PropertyReference API more or less obsolete. The only difference I am aware of right now is, that the PropertyReference class did the reflection in a privileged block. I did not do this in the adapter implementation, because I did not understand the reason. Maybe I will learn very quickly and have to fix it soon. :-) My feeling is, that the memory footprint of your current solution and a solution using adapters will be roughly the same, in cases where the Java Beans do not support change listeners. There is one object per property (PropertyReference vs. JavaBeanPropertyBuilder) and one object per row (ReadOnlyObjectWrapper vs. JavaBeanObjectProperty) with roughly the same information. Oh, just noticed... Are you sure, you are using ReadOnlyObjectWrapper? In that case you are probably wasting (a small amount of) memory and the new solution would be better. Without knowing any details, I think it would have been sufficient to use ReadOnlyObjectPropertyBase, which is smaller. Maybe you could go even further up in the hierarchy. What functionality do you need from the cell values? If it is only observability, an ObservableObjectValue is sufficient. That means, you could use com.sun.javafx.binding.ObjectConstant for immutable values. - Michael On 03.01.2012, at 02:19, Jonathan Giles wrote: > Michael, > > I'm interested to learn how you foresee these being used in the UI controls (mainly ListView and TableView). In particular, the TableView control (but any thoughts you have on the simpler ListView control would be appreciated too). > > To give a brief overview, TableView has an items ObservableList. Each TableColumn has a cellValueFactory (not to be confused with cellFactory) which is responsible for retrieving the data for a given column. The most common implementation of the cellValueFactory is javafx.scene.control.cell.PropertyValueFactory. This class uses the old PropertyReference API internally to reflectively get the Property if it is available (and this is then bound to so that changes are automatically reflected visually). If there is no *property() method, we try to call the get/is*() to get the value, and wrap this in a ReadOnlyObjectWrapper. This means that the value will be forever static (unless the developer rebuilds the TableView). > > Can I use the JavaBeanPropertyAdapter to add more functionality here (in particular, observability of JavaBean properties)? If so, is it cheap enough to have many of these bindings (I only create bindings for the visible cells, not for all of them). > > -- Jonathan > > > On 3/01/2012 12:32 a.m., Michael Heinrichs wrote: >> Hi everybody, >> >> I took the feedback for the first version of the Java Bean adapter and created a new design with the feedback incorporated. The general idea is to take whatever is available and use it. For example, if the Java Bean property supports change listeners, this will be used to synchronize changes from the Java Bean property to the JavaFX property. If there is no support for change listeners, you can still create an adapter, but you have to do the synchronization yourself or disallow changing the Java Bean property. >> >> I put everything in separated classes now and added a new package that contains the new classes and interfaces: javafx.beans.property.adapter. >> >> >> >> There are two new interfaces that define the common functionality of all JavaBeanProperty / ReadOnlyJavaBeanProperty instances: >> >> public interface ReadOnlyJavaBeanProperty extends ReadOnlyProperty { >> void fireValueChangedEvent(); >> void dispose(); >> } >> >> public interface JavaBeanProperty extends ReadOnlyJavaBeanProperty, Property {} >> >> The method fireValueChangedEvent() can be called to notify the adapter that the Java Bean property changed in case it does not support change listeners. The implementation uses WeakReferences and some automatic cleanup, if there are no references to the property left. But if you want more control, you can call dispose() on an adapter to remove all hooks to the bean. >> >> >> >> Then we have a bunch of implementations for all types of Java Bean adapters, e.g.: >> >> public final class ReadOnlyJavaBeanBooleanProperty extends ReadOnlyBooleanProperty implements ReadOnlyJavaBeanProperty {...} >> >> public final class ReadOnlyJavaBeanObjectProperty extends ReadOnlyObjectProperty implements ReadOnlyJavaBeanProperty {...} >> >> public final class JavaBeanBooleanProperty extends BooleanProperty implements JavaBeanProperty {...} >> >> public final class JavaBeanObjectProperty extends ObjectProperty implements JavaBeanProperty {...} >> >> >> >> And finally the really interesting part, the builders. There are two possibilities to use them. If you need to create an adapter for the same property of a collection of beans, you should first create the builder and then call createXYZProperty() for each bean. If you need to create just one adapter, there are static methods to do everything with a single call. >> >> public final class ReadOnlyJavaBeanPropertyBuilder { >> public static ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> public static ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> // same for all other types >> >> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} >> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName) throws NoSuchMethodException {...} >> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter) {...} >> >> public ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean) {...} >> public ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean) {...} >> // same for all other types >> } >> >> public final class JavaBeanPropertyBuilder { >> public static JavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> public static JavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> // same for all other types >> >> public JavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} >> public JavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName, String setterName) throws NoSuchMethodException {...} >> public JavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter, Method setter) {...} >> >> public JavaBeanBooleanProperty createBooleanProperty(Object bean) {...} >> public JavaBeanObjectProperty createObjectProperty(Object bean) {...} >> // same for all other types >> } >> >> What do you think? >> >> Thanks, >> Michael From greg.x.brown at oracle.com Tue Jan 3 06:39:18 2012 From: greg.x.brown at oracle.com (Greg Brown) Date: Tue, 3 Jan 2012 09:39:18 -0500 Subject: API break in 2.1 JavaFXBuilderFactory In-Reply-To: <4EFF101F.5030407@bestsolution.at> References: <4EFF101F.5030407@bestsolution.at> Message-ID: <827807B8-AF3C-48A5-9B1C-25A1DCF87B55@oracle.com> Not sure why you are seeing that exception. I don't have a problem loading that markup using the default constructor. The default constructor just invokes JavaFXBuilderFactory(ClassLoader, boolean) and passes false as the second argument. FWIW, these FXMLLoader constructors now create a default JavaFXBuilderFactory by default, so you may not actually need to create your own instance: FXMLLoader() FXMLLoader(URL) FXMLLoader(URL, ResourceBundle) G On Dec 31, 2011, at 8:37 AM, Tom Schindl wrote: > Hi, > > It looks like the JavaFXBuilderFactory lost the > JavaFXBuilderFactory(boolean) constructor. > > I've been this constructor like this "new JavaFXBuilderFactory(false)" > because with the default one (new JavaFXBuilderFactory()) an FXML-File > like this: > >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> > > Fails with an exception like this. > >> WARNUNG: Method text failed >> java.lang.NullPointerException >> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >> at java.lang.reflect.Method.invoke(Method.java:597) >> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:237) >> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:138) >> at javafx.fxml.FXMLLoader$ExpressionTargetMapping.changed(FXMLLoader.java:1127) >> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >> at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:43) >> at com.sun.javafx.fxml.expression.ExpressionBinding$ValueProperty.fireValueChangedEvent(ExpressionBinding.java:87) >> at com.sun.javafx.fxml.expression.ExpressionBinding$KeyPathMonitor$2.onChanged(ExpressionBinding.java:154) >> at com.sun.javafx.fxml.BeanAdapter$PropertyInvalidationListener.invalidated(BeanAdapter.java:170) >> at com.sun.javafx.binding.ExpressionHelper$MultipleInvalidation.fireValueChangedEvent(ExpressionHelper.java:301) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >> at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:991) >> at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:995) >> at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:935) >> at javafx.scene.control.TextInputControl$TextProperty.access$100(TextInputControl.java:907) >> at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:109) >> at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:140) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >> at javafx.scene.control.TextField$TextFieldContent.insert(TextField.java:70) >> at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:338) >> at com.sun.javafx.scene.control.skin.TextFieldSkin.replaceText(TextFieldSkin.java:407) >> at com.sun.javafx.scene.control.behavior.TextFieldBehavior.replaceText(TextFieldBehavior.java:134) >> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.defaultKeyTyped(TextInputControlBehavior.java:185) >> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callAction(TextInputControlBehavior.java:114) >> at com.sun.javafx.scene.control.behavior.BehaviorBase.callActionForEvent(BehaviorBase.java:165) >> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callActionForEvent(TextInputControlBehavior.java:107) >> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:127) >> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:125) >> at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:56) >> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:162) >> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:115) >> at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38) >> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37) >> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35) >> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >> at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:47) >> at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33) >> at javafx.event.Event.fireEvent(Event.java:171) >> at javafx.scene.Scene$KeyHandler.process(Scene.java:2938) >> at javafx.scene.Scene$KeyHandler.access$1700(Scene.java:2868) >> at javafx.scene.Scene.impl_processKeyEvent(Scene.java:1431) >> at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:1862) >> at com.sun.javafx.tk.quantum.EmbeddedScene$3.run(EmbeddedScene.java:366) >> at com.sun.javafx.application.PlatformImpl$3.run(PlatformImpl.java:119) >> at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method) >> at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:2545) >> at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3752) >> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:977) >> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:893) >> at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:91) >> at org.eclipse.ui.internal.Workbench$3.run(Workbench.java:565) >> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >> at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:520) >> at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149) >> at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:124) >> at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) >> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110) >> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79) >> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:352) >> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179) >> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >> at java.lang.reflect.Method.invoke(Method.java:597) >> at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:624) >> at org.eclipse.equinox.launcher.Main.basicRun(Main.java:579) >> at org.eclipse.equinox.launcher.Main.run(Main.java:1433) >> at org.eclipse.equinox.launcher.Main.main(Main.java:1409) > > Why has the constructor been removed? Should binding work also if one > uses the default constructor? > > Tom > > -- > B e s t S o l u t i o n . a t EDV Systemhaus GmbH > ------------------------------------------------------------------------ > tom schindl gesch?ftsf?hrer/CEO > ------------------------------------------------------------------------ > eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 > http://www.BestSolution.at phone ++43 512 935834 From tom.schindl at bestsolution.at Tue Jan 3 06:46:09 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Tue, 03 Jan 2012 15:46:09 +0100 Subject: API break in 2.1 JavaFXBuilderFactory In-Reply-To: <827807B8-AF3C-48A5-9B1C-25A1DCF87B55@oracle.com> References: <4EFF101F.5030407@bestsolution.at> <827807B8-AF3C-48A5-9B1C-25A1DCF87B55@oracle.com> Message-ID: <4F0314B1.2090709@bestsolution.at> I'm seeing this exception on 2.0.2 with the default constructor - on 2.1 all is fine! My problem is that to support my tooling on OS-X (2.1) and Windows (2.0.2 & 2.1) I now have to use reflection to use the none default constructor making my code look ugly but well if it has to be like this ... . Tom Am 03.01.12 15:39, schrieb Greg Brown: > Not sure why you are seeing that exception. I don't have a problem loading that markup using the default constructor. The default constructor just invokes JavaFXBuilderFactory(ClassLoader, boolean) and passes false as the second argument. > > FWIW, these FXMLLoader constructors now create a default JavaFXBuilderFactory by default, so you may not actually need to create your own instance: > > FXMLLoader() > FXMLLoader(URL) > FXMLLoader(URL, ResourceBundle) > > G > > On Dec 31, 2011, at 8:37 AM, Tom Schindl wrote: > >> Hi, >> >> It looks like the JavaFXBuilderFactory lost the >> JavaFXBuilderFactory(boolean) constructor. >> >> I've been this constructor like this "new JavaFXBuilderFactory(false)" >> because with the default one (new JavaFXBuilderFactory()) an FXML-File >> like this: >> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >> >> Fails with an exception like this. >> >>> WARNUNG: Method text failed >>> java.lang.NullPointerException >>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >>> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >>> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >>> at java.lang.reflect.Method.invoke(Method.java:597) >>> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:237) >>> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:138) >>> at javafx.fxml.FXMLLoader$ExpressionTargetMapping.changed(FXMLLoader.java:1127) >>> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181) >>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>> at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:43) >>> at com.sun.javafx.fxml.expression.ExpressionBinding$ValueProperty.fireValueChangedEvent(ExpressionBinding.java:87) >>> at com.sun.javafx.fxml.expression.ExpressionBinding$KeyPathMonitor$2.onChanged(ExpressionBinding.java:154) >>> at com.sun.javafx.fxml.BeanAdapter$PropertyInvalidationListener.invalidated(BeanAdapter.java:170) >>> at com.sun.javafx.binding.ExpressionHelper$MultipleInvalidation.fireValueChangedEvent(ExpressionHelper.java:301) >>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>> at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:991) >>> at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:995) >>> at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:935) >>> at javafx.scene.control.TextInputControl$TextProperty.access$100(TextInputControl.java:907) >>> at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:109) >>> at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:140) >>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>> at javafx.scene.control.TextField$TextFieldContent.insert(TextField.java:70) >>> at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:338) >>> at com.sun.javafx.scene.control.skin.TextFieldSkin.replaceText(TextFieldSkin.java:407) >>> at com.sun.javafx.scene.control.behavior.TextFieldBehavior.replaceText(TextFieldBehavior.java:134) >>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.defaultKeyTyped(TextInputControlBehavior.java:185) >>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callAction(TextInputControlBehavior.java:114) >>> at com.sun.javafx.scene.control.behavior.BehaviorBase.callActionForEvent(BehaviorBase.java:165) >>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callActionForEvent(TextInputControlBehavior.java:107) >>> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:127) >>> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:125) >>> at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:56) >>> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:162) >>> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:115) >>> at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38) >>> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37) >>> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >>> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35) >>> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >>> at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:47) >>> at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33) >>> at javafx.event.Event.fireEvent(Event.java:171) >>> at javafx.scene.Scene$KeyHandler.process(Scene.java:2938) >>> at javafx.scene.Scene$KeyHandler.access$1700(Scene.java:2868) >>> at javafx.scene.Scene.impl_processKeyEvent(Scene.java:1431) >>> at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:1862) >>> at com.sun.javafx.tk.quantum.EmbeddedScene$3.run(EmbeddedScene.java:366) >>> at com.sun.javafx.application.PlatformImpl$3.run(PlatformImpl.java:119) >>> at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method) >>> at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:2545) >>> at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3752) >>> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:977) >>> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >>> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:893) >>> at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:91) >>> at org.eclipse.ui.internal.Workbench$3.run(Workbench.java:565) >>> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >>> at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:520) >>> at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149) >>> at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:124) >>> at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) >>> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110) >>> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79) >>> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:352) >>> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179) >>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >>> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >>> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >>> at java.lang.reflect.Method.invoke(Method.java:597) >>> at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:624) >>> at org.eclipse.equinox.launcher.Main.basicRun(Main.java:579) >>> at org.eclipse.equinox.launcher.Main.run(Main.java:1433) >>> at org.eclipse.equinox.launcher.Main.main(Main.java:1409) >> >> Why has the constructor been removed? Should binding work also if one >> uses the default constructor? >> >> Tom >> >> -- >> B e s t S o l u t i o n . a t EDV Systemhaus GmbH >> ------------------------------------------------------------------------ >> tom schindl gesch?ftsf?hrer/CEO >> ------------------------------------------------------------------------ >> eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 >> http://www.BestSolution.at phone ++43 512 935834 > -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From greg.x.brown at oracle.com Tue Jan 3 06:55:39 2012 From: greg.x.brown at oracle.com (Greg Brown) Date: Tue, 3 Jan 2012 09:55:39 -0500 Subject: API break in 2.1 JavaFXBuilderFactory In-Reply-To: <4F0314B1.2090709@bestsolution.at> References: <4EFF101F.5030407@bestsolution.at> <827807B8-AF3C-48A5-9B1C-25A1DCF87B55@oracle.com> <4F0314B1.2090709@bestsolution.at> Message-ID: <0E154BF3-CB15-4067-B3F4-C42A40DF7A46@oracle.com> Ah, I see. Thanks for clarifying. In 2.0.x, the default JavaFXBuilderFactory constructor called JavaFXBuilderFactory(true). This was an unfortunate oversight, since the alwaysUseBuilders flag was meant for internal testing use only and should not have been set by the default constructor. However, it sounds like removing the boolean constructor completely will probably cause some confusion. I'll put it back and mark it as deprecated. Thanks, G On Jan 3, 2012, at 9:46 AM, Tom Schindl wrote: > I'm seeing this exception on 2.0.2 with the default constructor - on 2.1 > all is fine! > > My problem is that to support my tooling on OS-X (2.1) and Windows > (2.0.2 & 2.1) I now have to use reflection to use the none default > constructor making my code look ugly but well if it has to be like this > ... . > > Tom > > > Am 03.01.12 15:39, schrieb Greg Brown: >> Not sure why you are seeing that exception. I don't have a problem loading that markup using the default constructor. The default constructor just invokes JavaFXBuilderFactory(ClassLoader, boolean) and passes false as the second argument. >> >> FWIW, these FXMLLoader constructors now create a default JavaFXBuilderFactory by default, so you may not actually need to create your own instance: >> >> FXMLLoader() >> FXMLLoader(URL) >> FXMLLoader(URL, ResourceBundle) >> >> G >> >> On Dec 31, 2011, at 8:37 AM, Tom Schindl wrote: >> >>> Hi, >>> >>> It looks like the JavaFXBuilderFactory lost the >>> JavaFXBuilderFactory(boolean) constructor. >>> >>> I've been this constructor like this "new JavaFXBuilderFactory(false)" >>> because with the default one (new JavaFXBuilderFactory()) an FXML-File >>> like this: >>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>> >>> Fails with an exception like this. >>> >>>> WARNUNG: Method text failed >>>> java.lang.NullPointerException >>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >>>> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >>>> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >>>> at java.lang.reflect.Method.invoke(Method.java:597) >>>> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:237) >>>> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:138) >>>> at javafx.fxml.FXMLLoader$ExpressionTargetMapping.changed(FXMLLoader.java:1127) >>>> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181) >>>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>>> at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:43) >>>> at com.sun.javafx.fxml.expression.ExpressionBinding$ValueProperty.fireValueChangedEvent(ExpressionBinding.java:87) >>>> at com.sun.javafx.fxml.expression.ExpressionBinding$KeyPathMonitor$2.onChanged(ExpressionBinding.java:154) >>>> at com.sun.javafx.fxml.BeanAdapter$PropertyInvalidationListener.invalidated(BeanAdapter.java:170) >>>> at com.sun.javafx.binding.ExpressionHelper$MultipleInvalidation.fireValueChangedEvent(ExpressionHelper.java:301) >>>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>>> at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:991) >>>> at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:995) >>>> at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:935) >>>> at javafx.scene.control.TextInputControl$TextProperty.access$100(TextInputControl.java:907) >>>> at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:109) >>>> at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:140) >>>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>>> at javafx.scene.control.TextField$TextFieldContent.insert(TextField.java:70) >>>> at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:338) >>>> at com.sun.javafx.scene.control.skin.TextFieldSkin.replaceText(TextFieldSkin.java:407) >>>> at com.sun.javafx.scene.control.behavior.TextFieldBehavior.replaceText(TextFieldBehavior.java:134) >>>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.defaultKeyTyped(TextInputControlBehavior.java:185) >>>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callAction(TextInputControlBehavior.java:114) >>>> at com.sun.javafx.scene.control.behavior.BehaviorBase.callActionForEvent(BehaviorBase.java:165) >>>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callActionForEvent(TextInputControlBehavior.java:107) >>>> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:127) >>>> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:125) >>>> at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:56) >>>> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:162) >>>> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:115) >>>> at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38) >>>> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37) >>>> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >>>> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35) >>>> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >>>> at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:47) >>>> at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33) >>>> at javafx.event.Event.fireEvent(Event.java:171) >>>> at javafx.scene.Scene$KeyHandler.process(Scene.java:2938) >>>> at javafx.scene.Scene$KeyHandler.access$1700(Scene.java:2868) >>>> at javafx.scene.Scene.impl_processKeyEvent(Scene.java:1431) >>>> at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:1862) >>>> at com.sun.javafx.tk.quantum.EmbeddedScene$3.run(EmbeddedScene.java:366) >>>> at com.sun.javafx.application.PlatformImpl$3.run(PlatformImpl.java:119) >>>> at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method) >>>> at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:2545) >>>> at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3752) >>>> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:977) >>>> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >>>> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:893) >>>> at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:91) >>>> at org.eclipse.ui.internal.Workbench$3.run(Workbench.java:565) >>>> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >>>> at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:520) >>>> at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149) >>>> at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:124) >>>> at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) >>>> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110) >>>> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79) >>>> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:352) >>>> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179) >>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >>>> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >>>> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >>>> at java.lang.reflect.Method.invoke(Method.java:597) >>>> at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:624) >>>> at org.eclipse.equinox.launcher.Main.basicRun(Main.java:579) >>>> at org.eclipse.equinox.launcher.Main.run(Main.java:1433) >>>> at org.eclipse.equinox.launcher.Main.main(Main.java:1409) >>> >>> Why has the constructor been removed? Should binding work also if one >>> uses the default constructor? >>> >>> Tom >>> >>> -- >>> B e s t S o l u t i o n . a t EDV Systemhaus GmbH >>> ------------------------------------------------------------------------ >>> tom schindl gesch?ftsf?hrer/CEO >>> ------------------------------------------------------------------------ >>> eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 >>> http://www.BestSolution.at phone ++43 512 935834 >> > > > -- > B e s t S o l u t i o n . a t EDV Systemhaus GmbH > ------------------------------------------------------------------------ > tom schindl gesch?ftsf?hrer/CEO > ------------------------------------------------------------------------ > eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 > http://www.BestSolution.at phone ++43 512 935834 From tom.schindl at bestsolution.at Tue Jan 3 06:58:03 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Tue, 03 Jan 2012 15:58:03 +0100 Subject: API break in 2.1 JavaFXBuilderFactory In-Reply-To: <0E154BF3-CB15-4067-B3F4-C42A40DF7A46@oracle.com> References: <4EFF101F.5030407@bestsolution.at> <827807B8-AF3C-48A5-9B1C-25A1DCF87B55@oracle.com> <4F0314B1.2090709@bestsolution.at> <0E154BF3-CB15-4067-B3F4-C42A40DF7A46@oracle.com> Message-ID: <4F03177B.7020804@bestsolution.at> Hi Greg, Great thanks! Tom Am 03.01.12 15:55, schrieb Greg Brown: > Ah, I see. Thanks for clarifying. > > In 2.0.x, the default JavaFXBuilderFactory constructor called JavaFXBuilderFactory(true). This was an unfortunate oversight, since the alwaysUseBuilders flag was meant for internal testing use only and should not have been set by the default constructor. However, it sounds like removing the boolean constructor completely will probably cause some confusion. I'll put it back and mark it as deprecated. > > Thanks, > G > > On Jan 3, 2012, at 9:46 AM, Tom Schindl wrote: > >> I'm seeing this exception on 2.0.2 with the default constructor - on 2.1 >> all is fine! >> >> My problem is that to support my tooling on OS-X (2.1) and Windows >> (2.0.2 & 2.1) I now have to use reflection to use the none default >> constructor making my code look ugly but well if it has to be like this >> ... . >> >> Tom >> >> >> Am 03.01.12 15:39, schrieb Greg Brown: >>> Not sure why you are seeing that exception. I don't have a problem loading that markup using the default constructor. The default constructor just invokes JavaFXBuilderFactory(ClassLoader, boolean) and passes false as the second argument. >>> >>> FWIW, these FXMLLoader constructors now create a default JavaFXBuilderFactory by default, so you may not actually need to create your own instance: >>> >>> FXMLLoader() >>> FXMLLoader(URL) >>> FXMLLoader(URL, ResourceBundle) >>> >>> G >>> >>> On Dec 31, 2011, at 8:37 AM, Tom Schindl wrote: >>> >>>> Hi, >>>> >>>> It looks like the JavaFXBuilderFactory lost the >>>> JavaFXBuilderFactory(boolean) constructor. >>>> >>>> I've been this constructor like this "new JavaFXBuilderFactory(false)" >>>> because with the default one (new JavaFXBuilderFactory()) an FXML-File >>>> like this: >>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> >>>> >>>> Fails with an exception like this. >>>> >>>>> WARNUNG: Method text failed >>>>> java.lang.NullPointerException >>>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >>>>> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >>>>> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >>>>> at java.lang.reflect.Method.invoke(Method.java:597) >>>>> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:237) >>>>> at javafx.fxml.JavaFXBuilder$ObjectBuilder.put(JavaFXBuilderFactory.java:138) >>>>> at javafx.fxml.FXMLLoader$ExpressionTargetMapping.changed(FXMLLoader.java:1127) >>>>> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181) >>>>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>>>> at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:43) >>>>> at com.sun.javafx.fxml.expression.ExpressionBinding$ValueProperty.fireValueChangedEvent(ExpressionBinding.java:87) >>>>> at com.sun.javafx.fxml.expression.ExpressionBinding$KeyPathMonitor$2.onChanged(ExpressionBinding.java:154) >>>>> at com.sun.javafx.fxml.BeanAdapter$PropertyInvalidationListener.invalidated(BeanAdapter.java:170) >>>>> at com.sun.javafx.binding.ExpressionHelper$MultipleInvalidation.fireValueChangedEvent(ExpressionHelper.java:301) >>>>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>>>> at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:991) >>>>> at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:995) >>>>> at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:935) >>>>> at javafx.scene.control.TextInputControl$TextProperty.access$100(TextInputControl.java:907) >>>>> at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:109) >>>>> at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:140) >>>>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:64) >>>>> at javafx.scene.control.TextField$TextFieldContent.insert(TextField.java:70) >>>>> at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:338) >>>>> at com.sun.javafx.scene.control.skin.TextFieldSkin.replaceText(TextFieldSkin.java:407) >>>>> at com.sun.javafx.scene.control.behavior.TextFieldBehavior.replaceText(TextFieldBehavior.java:134) >>>>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.defaultKeyTyped(TextInputControlBehavior.java:185) >>>>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callAction(TextInputControlBehavior.java:114) >>>>> at com.sun.javafx.scene.control.behavior.BehaviorBase.callActionForEvent(BehaviorBase.java:165) >>>>> at com.sun.javafx.scene.control.behavior.TextInputControlBehavior.callActionForEvent(TextInputControlBehavior.java:107) >>>>> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:127) >>>>> at com.sun.javafx.scene.control.behavior.BehaviorBase$1.handle(BehaviorBase.java:125) >>>>> at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:56) >>>>> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:162) >>>>> at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:115) >>>>> at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38) >>>>> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37) >>>>> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >>>>> at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35) >>>>> at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92) >>>>> at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:47) >>>>> at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33) >>>>> at javafx.event.Event.fireEvent(Event.java:171) >>>>> at javafx.scene.Scene$KeyHandler.process(Scene.java:2938) >>>>> at javafx.scene.Scene$KeyHandler.access$1700(Scene.java:2868) >>>>> at javafx.scene.Scene.impl_processKeyEvent(Scene.java:1431) >>>>> at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:1862) >>>>> at com.sun.javafx.tk.quantum.EmbeddedScene$3.run(EmbeddedScene.java:366) >>>>> at com.sun.javafx.application.PlatformImpl$3.run(PlatformImpl.java:119) >>>>> at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method) >>>>> at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:2545) >>>>> at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3752) >>>>> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:977) >>>>> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >>>>> at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:893) >>>>> at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:91) >>>>> at org.eclipse.ui.internal.Workbench$3.run(Workbench.java:565) >>>>> at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) >>>>> at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:520) >>>>> at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149) >>>>> at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:124) >>>>> at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) >>>>> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110) >>>>> at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79) >>>>> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:352) >>>>> at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179) >>>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >>>>> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) >>>>> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) >>>>> at java.lang.reflect.Method.invoke(Method.java:597) >>>>> at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:624) >>>>> at org.eclipse.equinox.launcher.Main.basicRun(Main.java:579) >>>>> at org.eclipse.equinox.launcher.Main.run(Main.java:1433) >>>>> at org.eclipse.equinox.launcher.Main.main(Main.java:1409) >>>> >>>> Why has the constructor been removed? Should binding work also if one >>>> uses the default constructor? >>>> >>>> Tom >>>> >>>> -- >>>> B e s t S o l u t i o n . a t EDV Systemhaus GmbH >>>> ------------------------------------------------------------------------ >>>> tom schindl gesch?ftsf?hrer/CEO >>>> ------------------------------------------------------------------------ >>>> eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 >>>> http://www.BestSolution.at phone ++43 512 935834 >>> >> >> >> -- >> B e s t S o l u t i o n . a t EDV Systemhaus GmbH >> ------------------------------------------------------------------------ >> tom schindl gesch?ftsf?hrer/CEO >> ------------------------------------------------------------------------ >> eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 >> http://www.BestSolution.at phone ++43 512 935834 > -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From hang.vo at oracle.com Tue Jan 3 07:22:45 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 03 Jan 2012 15:22:45 +0000 Subject: hg: openjfx/2.1/master/rt: 4 new changesets Message-ID: <20120103152247.626054785C@hg.openjdk.java.net> Changeset: e6286f30f78a Author: leifs Date: 2011-12-21 12:52 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e6286f30f78a Fixed RT-18517 Create new higher level API for menus to be used by controls and Toolkit + javafx-ui-controls/src/com/sun/javafx/scene/control/GlobalMenuAdapter.java Changeset: 69f5533bc6ae Author: jpgodine at JPGODINE-LAP.st-users.us.oracle.com Date: 2011-12-27 10:29 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/69f5533bc6ae Automated merge with ssh://jgodinez at jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt Changeset: 6e8e7427a8e0 Author: hudson Date: 2011-12-28 10:20 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6e8e7427a8e0 Added tag 2.1-b07 for changeset 69f5533bc6ae ! .hgtags Changeset: ea490d064cf0 Author: David Grieve Date: 2012-01-03 10:12 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/ea490d064cf0 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt From hang.vo at oracle.com Tue Jan 3 08:22:18 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 03 Jan 2012 16:22:18 +0000 Subject: hg: openjfx/2.1/master/rt: DateStringConverterTest and DateTimeStringConverterTest are failing. Mark failing tests with @Ignore pending RT-18782 Message-ID: <20120103162219.162D64785D@hg.openjdk.java.net> Changeset: 3214b0e6b680 Author: David Grieve Date: 2012-01-03 11:15 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3214b0e6b680 DateStringConverterTest and DateTimeStringConverterTest are failing. Mark failing tests with @Ignore pending RT-18782 ! javafx-util-converter/test/javafx/util/converter/DateStringConverterTest.java ! javafx-util-converter/test/javafx/util/converter/DateTimeStringConverterTest.java From richard.bair at oracle.com Tue Jan 3 07:42:58 2012 From: richard.bair at oracle.com (Richard Bair) Date: Tue, 3 Jan 2012 07:42:58 -0800 Subject: JavaBeanPropertyAdapter Reloaded In-Reply-To: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> References: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> Message-ID: <10EFDBB3-AC59-44A1-AABE-0206DDAAC9ED@oracle.com> Hi Michael, I like the approach. > There are two new interfaces that define the common functionality of all JavaBeanProperty / ReadOnlyJavaBeanProperty instances: > > public interface ReadOnlyJavaBeanProperty extends ReadOnlyProperty { > void fireValueChangedEvent(); > void dispose(); > } > > public interface JavaBeanProperty extends ReadOnlyJavaBeanProperty, Property {} > > The method fireValueChangedEvent() can be called to notify the adapter that the Java Bean property changed in case it does not support change listeners. The implementation uses WeakReferences and some automatic cleanup, if there are no references to the property left. But if you want more control, you can call dispose() on an adapter to remove all hooks to the bean. Under what circumstance would I want to manually call dispose? > And finally the really interesting part, the builders. There are two possibilities to use them. If you need to create an adapter for the same property of a collection of beans, you should first create the builder and then call createXYZProperty() for each bean. If you need to create just one adapter, there are static methods to do everything with a single call. Ok, so his is the mechanism that Jonathan's cell value factory implementation would use. > public final class ReadOnlyJavaBeanPropertyBuilder { > public static ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > public static ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > // same for all other types > > public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} > public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName) throws NoSuchMethodException {...} > public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter) {...} > > public ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean) {...} > public ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean) {...} > // same for all other types > } > > public final class JavaBeanPropertyBuilder { > public static JavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > public static JavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} > // same for all other types > > public JavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} > public JavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName, String setterName) throws NoSuchMethodException {...} > public JavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter, Method setter) {...} > > public JavaBeanBooleanProperty createBooleanProperty(Object bean) {...} > public JavaBeanObjectProperty createObjectProperty(Object bean) {...} > // same for all other types > } How do these fit with the existing builders that are generated? Do these use the same semantics / naming pattern / implement the same interface? Thanks Richard > > What do you think? > > Thanks, > Michael From hang.vo at oracle.com Tue Jan 3 14:33:04 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 03 Jan 2012 22:33:04 +0000 Subject: hg: openjfx/2.1/master/rt: Partial RT-17449: Swapped out NodeEventDispatcher for just EventHandlerManager. Optimized buildEventDispatchChain to not force creation of the EventHandlerManager if there are no listeners. Reviewed by Lubo. Message-ID: <20120103223305.4451047869@hg.openjdk.java.net> Changeset: 210fb11e5af4 Author: rbair Date: 2012-01-03 14:23 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/210fb11e5af4 Partial RT-17449: Swapped out NodeEventDispatcher for just EventHandlerManager. Optimized buildEventDispatchChain to not force creation of the EventHandlerManager if there are no listeners. Reviewed by Lubo. ! javafx-concurrent/src/javafx/concurrent/EventHelper.java From hang.vo at oracle.com Tue Jan 3 14:42:08 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 03 Jan 2012 22:42:08 +0000 Subject: hg: openjfx/2.1/master/rt: Updated module so that it uses the project JDK Message-ID: <20120103224208.DA2154786A@hg.openjdk.java.net> Changeset: e080b9861f62 Author: rbair Date: 2012-01-03 14:35 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e080b9861f62 Updated module so that it uses the project JDK ! javafx-beans-dt/javafx-beans-dt.iml From hang.vo at oracle.com Tue Jan 3 15:02:40 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 03 Jan 2012 23:02:40 +0000 Subject: hg: openjfx/2.1/master/rt: Fix RT-17449: Fixed a bug found in review by Lubo where setting the event handler through the property object was not working. Added unit tests to cover this situation. Message-ID: <20120103230241.0EABB4786B@hg.openjdk.java.net> Changeset: 42a9c6310d5e Author: rbair Date: 2012-01-03 14:51 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/42a9c6310d5e Fix RT-17449: Fixed a bug found in review by Lubo where setting the event handler through the property object was not working. Added unit tests to cover this situation. ! javafx-concurrent/src/javafx/concurrent/EventHelper.java ! javafx-concurrent/test/javafx/concurrent/TaskEventTest.java From Richard.Bair at oracle.com Tue Jan 3 20:05:29 2012 From: Richard.Bair at oracle.com (Richard Bair) Date: Tue, 3 Jan 2012 20:05:29 -0800 Subject: Review of solution for Cancelled Tasks Message-ID: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> http://javafx-jira.kenai.com/browse/RT-17932 I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) - The developer of a Task can check isCancelled from the background thread and self-terminate - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. If you write a Task, and do nothing smart: new Task() { public Object call() throws Exception { for (int i=0; i<100; i++) { doSomething(); } return null; } }; In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: new Task() { public Object call() throws Exception { for (int i=0; i<100; i++) { doSomething(); updateProgress(i, 100); } return null; } }; This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. In this case...: new Task() { public Object call() throws Exception { for (int i=0; i<100; i++) { try { doSomething(); updateProgress(i, 100); } catch (Exception e) { } } return null; } }; ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: new Task() { public Object call() throws Exception { for (int i=0; i<100; i++) { if (isCancelled()) break; try { doSomething(); updateProgress(i, 100); } catch (Exception e) { } } return null; } }; Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: new Task() { public Object call() throws Exception { for (int i=0; i<100; i++) { if (isCancelled()) { Platform.runLater(new Runnable() { public void run() { updateMessage("Cancelled") } }); break; } try { doSomething(); updateProgress(i, 100); } catch (Exception e) { } } return null; } }; new Task() { public Object call() throws Exception { for (int i=0; i<100; i++) { if (isCancelled()) break; try { doSomething(); updateProgress(i, 100); } catch (Exception e) { } } return null; } @Override protected void cancelled() { updateMessage("Cancelled"); } }; I think this works fairly well. Anything I'm missing? Thanks! Richard From jonathan.giles at oracle.com Tue Jan 3 22:03:18 2012 From: jonathan.giles at oracle.com (Jonathan Giles) Date: Wed, 04 Jan 2012 16:03:18 +1000 Subject: Testing keyboard events in controls Message-ID: <4F03EBA6.1080508@oracle.com> Hi all (and happy new year!), ListView/TreeView/TableView have a heap of keyboard requirements that come in from UX. Keyboard event handling code is often fraught with complexity due to the many permutations. I'm frequently getting burnt due to subtle changes in this code causing unintended regressions. I finally decided to do something about this, and have thrown together a very simple set of APIs to make it possible to write simple unit tests that test keyboard navigation. It's all very lightweight so that it can easily run as part of the JavaFX engineering continuous build (which means it fails sooner, compared to SQE tests, or even worse - when it ends up in the hands of developers!). Additionally, it's all very primitive and proof-of-concept at this stage, and I am happy to refine (and relocate) the class to provide utility in this area if it doesn't duplicate existing functionality I'm unaware of. I'm also certain I'm missing some of the details on how best to do this, so feedback is welcome. Anywho, you can find the class in the rt/javafx-ui-controls project, in the test directory, in javafx.scene.control.KeyEventFirer. You can see a few unit tests that were used to flesh out the API in javafx.scene.control.ListViewKeyInputTest. I plan to add many more here, and for other controls, in the coming weeks and months. These are the key pointers: 1) When creating a KeyEventFirer, you must provide an EventTarget. Despite the scary name, all Nodes are EventTargets. 2) I put the ListView inside a group, which is placed in a scene, which itself is placed in a stage. I show() and hide() the stage as necessary (in the setup() / tearDown() methods). Without this, events don't fire. 3) I use the separate javafx.scene.control.KeyModifier class to provide zero or more keyboard modifiers (shift, alt, ctrl, meta) to the keyboard input. An enum might already exist for these modifiers, but I'm not sure... 4) I have convenience methods (for my needs, anyway) for up/down/left/right keyboard inputs, in the form of 'do*ArrowPress(KeyModifier... modifiers)' methods. I'm sure more can be added... 5) For all other keyboard input, you can use the 'doKeyPress(KeyCode keyCode, KeyModifier... modifiers)' method. Any feedback would be appreciated. -- -- Jonathan From hang.vo at oracle.com Tue Jan 3 22:12:17 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 04 Jan 2012 06:12:17 +0000 Subject: hg: openjfx/2.1/master/rt: 3 new changesets Message-ID: <20120104061219.0327B4786D@hg.openjdk.java.net> Changeset: 3c630d05895d Author: jgiles Date: 2012-01-04 16:00 +1000 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3c630d05895d [TEST ONLY] Added simple test API that allows for easy keyboard event testing. + javafx-ui-controls/test/javafx/scene/control/KeyEventFirer.java + javafx-ui-controls/test/javafx/scene/control/KeyModifier.java + javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java Changeset: 38665fd4d922 Author: jgiles Date: 2012-01-04 16:01 +1000 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/38665fd4d922 Minor fixes in ListViewBehavior based on previous unit tests. ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java Changeset: 1d1a4622b2aa Author: jgiles Date: 2012-01-04 16:02 +1000 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/1d1a4622b2aa Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From tbee at tbee.org Tue Jan 3 22:29:52 2012 From: tbee at tbee.org (Tom Eugelink) Date: Wed, 04 Jan 2012 07:29:52 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> Message-ID: <4F03F1E0.5020809@tbee.org> I'm not sure if we should go as far as trying to "somewhat" fix faulty code, because formally the background task should check if it's cancelled. So IMHO RT-17932 is not a JavaFX bug, but a coder's bug. The only drawback to the proposed solution I can see, is that you cannot actually set the progress anymore .So if you are checking isCancelled and then want the progress to show 100% (which is what I often do), you now suddenly get an exception. If you decided to go this way, the only change to your solution could be to throw an checked exception, for example "TaskIsCancelled", which forces the coder to deal with that situation. But for one I do not like checked exceptions and secondly it would require an API change. Personally I'd flag RT-17932 as "not a bug" and tell the owner of the issue to start checking isCancelled. Tom On 2012-01-04 05:05, Richard Bair wrote: > http://javafx-jira.kenai.com/browse/RT-17932 > > I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: > > - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) > - The developer of a Task can check isCancelled from the background thread and self-terminate > - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled > - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! > - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). > > In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. > > If you write a Task, and do nothing smart: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > doSomething(); > } > return null; > } > }; > > In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > doSomething(); > updateProgress(i, 100); > } > return null; > } > }; > > This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. > > In this case...: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) { > Platform.runLater(new Runnable() { > public void run() { > updateMessage("Cancelled") > } > }); > break; > } > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > > @Override protected void cancelled() { > updateMessage("Cancelled"); > } > }; > > I think this works fairly well. Anything I'm missing? > > Thanks! > Richard From jandam at crcdata.cz Wed Jan 4 00:05:29 2012 From: jandam at crcdata.cz (Janda Martin) Date: Wed, 4 Jan 2012 09:05:29 +0100 (CET) Subject: Review of solution for Cancelled Tasks In-Reply-To: <29711977.61325664246385.JavaMail.root@zs.crcpraha> Message-ID: <26788115.81325664329703.JavaMail.root@zs.crcpraha> I agree with Tom Eugelink. After cancelling task you should be able to fully control progress and status text. For example db application you rollback transaction. I think that IllegalStateException should be thrown when task is in IDLE or DONE state Java SE: progress value (double): - NaN = no progress - +Inf = indetermite progress - 0..1 = progress 0..100% Pseudocode: statusText = "Preparing..." progressValue = Double.POSITIVE_INFINITY ... statusText = "Working... progressValue = 0.0 try { loop ... if(cancelled) { statusText = "Cancelling..." progressValue = Double.POSITIVE_INFINITY ... return; } progressValue = 0.0 --- 1.0 ... end loop statusText = "Finishing..." progressValue = Double.POSITIVE_INFINITY ... } finally { progressValue = Double.NaN if(cancelled) statusText = "CANCELLED" else statusText = "DONE" } I'm sorry I didn't study JavaFX api already. Thank you guys that you made great progress on JavaFX. (I'm waiting for SceneBuilder and linux dev preview) Martin ----- Original Message ----- From: "Tom Eugelink" To: -dev at openjdk.java.openjfxnet Sent: Wednesday, January 4, 2012 7:29:52 AM GMT +01:00 Amsterdam / Berlin / Bern / Rome / Stockholm / Vienna Subject: Re: Review of solution for Cancelled Tasks I'm not sure if we should go as far as trying to "somewhat" fix faulty code, because formally the background task should check if it's cancelled. So IMHO RT-17932 is not a JavaFX bug, but a coder's bug. The only drawback to the proposed solution I can see, is that you cannot actually set the progress anymore .So if you are checking isCancelled and then want the progress to show 100% (which is what I often do), you now suddenly get an exception. If you decided to go this way, the only change to your solution could be to throw an checked exception, for example "TaskIsCancelled", which forces the coder to deal with that situation. But for one I do not like checked exceptions and secondly it would require an API change. Personally I'd flag RT-17932 as "not a bug" and tell the owner of the issue to start checking isCancelled. Tom On 2012-01-04 05:05, Richard Bair wrote: > http://javafx-jira.kenai.com/browse/RT-17932 > > I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: > > - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) > - The developer of a Task can check isCancelled from the background thread and self-terminate > - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled > - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! > - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). > > In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. > > If you write a Task, and do nothing smart: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > doSomething(); > } > return null; > } > }; > > In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > doSomething(); > updateProgress(i, 100); > } > return null; > } > }; > > This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. > > In this case...: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) { > Platform.runLater(new Runnable() { > public void run() { > updateMessage("Cancelled") > } > }); > break; > } > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > > @Override protected void cancelled() { > updateMessage("Cancelled"); > } > }; > > I think this works fairly well. Anything I'm missing? > > Thanks! > Richard From michael.heinrichs at oracle.com Wed Jan 4 01:12:29 2012 From: michael.heinrichs at oracle.com (Michael Heinrichs) Date: Wed, 4 Jan 2012 10:12:29 +0100 Subject: JavaBeanPropertyAdapter Reloaded In-Reply-To: <10EFDBB3-AC59-44A1-AABE-0206DDAAC9ED@oracle.com> References: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> <10EFDBB3-AC59-44A1-AABE-0206DDAAC9ED@oracle.com> Message-ID: Hi Richard, comments are inlined. - Michael On 03.01.2012, at 16:42, Richard Bair wrote: > Hi Michael, > > I like the approach. > >> There are two new interfaces that define the common functionality of all JavaBeanProperty / ReadOnlyJavaBeanProperty instances: >> >> public interface ReadOnlyJavaBeanProperty extends ReadOnlyProperty { >> void fireValueChangedEvent(); >> void dispose(); >> } >> >> public interface JavaBeanProperty extends ReadOnlyJavaBeanProperty, Property {} >> >> The method fireValueChangedEvent() can be called to notify the adapter that the Java Bean property changed in case it does not support change listeners. The implementation uses WeakReferences and some automatic cleanup, if there are no references to the property left. But if you want more control, you can call dispose() on an adapter to remove all hooks to the bean. > > Under what circumstance would I want to manually call dispose? > Right now I know about two cases, the first rather theoretic for now. If JavaFX will be ported to a platform that does not support WeakReferences, you have to clean up everything yourself. All of the classes in the Property and Binding API use WeakReferences and usually one does not have to remove listeners, unbind things etc. But all of them also allow to manually clean thing up, in case the default solution is not supported on a platform. The second more real case for now is testing. If you have some functionality that needs to be executed on clean up, you can enforce it manually. >> And finally the really interesting part, the builders. There are two possibilities to use them. If you need to create an adapter for the same property of a collection of beans, you should first create the builder and then call createXYZProperty() for each bean. If you need to create just one adapter, there are static methods to do everything with a single call. > > Ok, so his is the mechanism that Jonathan's cell value factory implementation would use. Right, assuming all rows/cells are based on the same bean class, you should create only one Builder. > >> public final class ReadOnlyJavaBeanPropertyBuilder { >> public static ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> public static ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> // same for all other types >> >> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} >> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName) throws NoSuchMethodException {...} >> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter) {...} >> >> public ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean) {...} >> public ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean) {...} >> // same for all other types >> } >> >> public final class JavaBeanPropertyBuilder { >> public static JavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> public static JavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >> // same for all other types >> >> public JavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} >> public JavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName, String setterName) throws NoSuchMethodException {...} >> public JavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter, Method setter) {...} >> >> public JavaBeanBooleanProperty createBooleanProperty(Object bean) {...} >> public JavaBeanObjectProperty createObjectProperty(Object bean) {...} >> // same for all other types >> } > > How do these fit with the existing builders that are generated? Do these use the same semantics / naming pattern / implement the same interface? > No, right now they use a different pattern. I am undecided, if we should enforce the same pattern. A Java Bean adapter requires some mandatory parameters for which there are no default values. AFAIK there is no special treatment for such parameters in the builders, which means we cannot check during compile time, if all mandatory parameters were set. With the approach above on the other hand, you are forced to specify the mandatory parameters in the constructor / create()-method. JavaBeanBooleanPropertyBuilder.create().propertyName("x").bean(obj).build(); If we would use the same pattern, we would only be able to check during runtime, if the propertyName was set for example. (Note that we would also need a builder for each type, which is probably not a bad idea anyway.) Do you think, we we should drop the compile time check to gain the more familiar pattern? > Thanks > Richard >> >> What do you think? >> >> Thanks, >> Michael From roman at kennke.org Wed Jan 4 04:10:53 2012 From: roman at kennke.org (Roman Kennke) Date: Wed, 04 Jan 2012 13:10:53 +0100 Subject: Testing keyboard events in controls In-Reply-To: <4F03EBA6.1080508@oracle.com> References: <4F03EBA6.1080508@oracle.com> Message-ID: <1325679053.3443.2.camel@moonlight> Hi Jonathan, Happy New Year to you and the whole JFX team! Not having looked at your code yet, I am wondering if it would make sense to add a (java.awt.)Robot-like class to JavaFX? Maybe the AWT Robot can even be used directly? (after all, it generates OS level events, I believe it doesn't matter if it's an AWT window, JavaFX window or any other window). A while ago I was thinking of building a FEST (for Swing) like testing framework for JavaFX (for the ThingsFX project), and I'll certainly start this once I have some time again. This should also be useful for testing inside JavaFX itself. Cheers, Roman Am Mittwoch, den 04.01.2012, 16:03 +1000 schrieb Jonathan Giles: > Hi all (and happy new year!), > > ListView/TreeView/TableView have a heap of keyboard requirements that > come in from UX. Keyboard event handling code is often fraught with > complexity due to the many permutations. I'm frequently getting burnt > due to subtle changes in this code causing unintended regressions. > > I finally decided to do something about this, and have thrown together a > very simple set of APIs to make it possible to write simple unit tests > that test keyboard navigation. It's all very lightweight so that it can > easily run as part of the JavaFX engineering continuous build (which > means it fails sooner, compared to SQE tests, or even worse - when it > ends up in the hands of developers!). Additionally, it's all very > primitive and proof-of-concept at this stage, and I am happy to refine > (and relocate) the class to provide utility in this area if it doesn't > duplicate existing functionality I'm unaware of. I'm also certain I'm > missing some of the details on how best to do this, so feedback is welcome. > > Anywho, you can find the class in the rt/javafx-ui-controls project, in > the test directory, in javafx.scene.control.KeyEventFirer. You can see a > few unit tests that were used to flesh out the API in > javafx.scene.control.ListViewKeyInputTest. I plan to add many more here, > and for other controls, in the coming weeks and months. > > These are the key pointers: > 1) When creating a KeyEventFirer, you must provide an EventTarget. > Despite the scary name, all Nodes are EventTargets. > 2) I put the ListView inside a group, which is placed in a scene, which > itself is placed in a stage. I show() and hide() the stage as necessary > (in the setup() / tearDown() methods). Without this, events don't fire. > 3) I use the separate javafx.scene.control.KeyModifier class to provide > zero or more keyboard modifiers (shift, alt, ctrl, meta) to the keyboard > input. An enum might already exist for these modifiers, but I'm not sure... > 4) I have convenience methods (for my needs, anyway) for > up/down/left/right keyboard inputs, in the form of > 'do*ArrowPress(KeyModifier... modifiers)' methods. I'm sure more can be > added... > 5) For all other keyboard input, you can use the 'doKeyPress(KeyCode > keyCode, KeyModifier... modifiers)' method. > > Any feedback would be appreciated. > From pavel.safrata at oracle.com Wed Jan 4 04:28:57 2012 From: pavel.safrata at oracle.com (Pavel Safrata) Date: Wed, 04 Jan 2012 13:28:57 +0100 Subject: Testing keyboard events in controls In-Reply-To: <1325679053.3443.2.camel@moonlight> References: <4F03EBA6.1080508@oracle.com> <1325679053.3443.2.camel@moonlight> Message-ID: <4F044609.5060206@oracle.com> Hi Roman, just FYI, we are already tracking a request for robot API here: http://javafx-jira.kenai.com/browse/RT-17571 With regards, Pavel On 4.1.2012 13:10, Roman Kennke wrote: > Hi Jonathan, > > Happy New Year to you and the whole JFX team! > > Not having looked at your code yet, I am wondering if it would make > sense to add a (java.awt.)Robot-like class to JavaFX? Maybe the AWT > Robot can even be used directly? (after all, it generates OS level > events, I believe it doesn't matter if it's an AWT window, JavaFX window > or any other window). > > A while ago I was thinking of building a FEST (for Swing) like testing > framework for JavaFX (for the ThingsFX project), and I'll certainly > start this once I have some time again. This should also be useful for > testing inside JavaFX itself. > > Cheers, Roman > > Am Mittwoch, den 04.01.2012, 16:03 +1000 schrieb Jonathan Giles: >> Hi all (and happy new year!), >> >> ListView/TreeView/TableView have a heap of keyboard requirements that >> come in from UX. Keyboard event handling code is often fraught with >> complexity due to the many permutations. I'm frequently getting burnt >> due to subtle changes in this code causing unintended regressions. >> >> I finally decided to do something about this, and have thrown together a >> very simple set of APIs to make it possible to write simple unit tests >> that test keyboard navigation. It's all very lightweight so that it can >> easily run as part of the JavaFX engineering continuous build (which >> means it fails sooner, compared to SQE tests, or even worse - when it >> ends up in the hands of developers!). Additionally, it's all very >> primitive and proof-of-concept at this stage, and I am happy to refine >> (and relocate) the class to provide utility in this area if it doesn't >> duplicate existing functionality I'm unaware of. I'm also certain I'm >> missing some of the details on how best to do this, so feedback is welcome. >> >> Anywho, you can find the class in the rt/javafx-ui-controls project, in >> the test directory, in javafx.scene.control.KeyEventFirer. You can see a >> few unit tests that were used to flesh out the API in >> javafx.scene.control.ListViewKeyInputTest. I plan to add many more here, >> and for other controls, in the coming weeks and months. >> >> These are the key pointers: >> 1) When creating a KeyEventFirer, you must provide an EventTarget. >> Despite the scary name, all Nodes are EventTargets. >> 2) I put the ListView inside a group, which is placed in a scene, which >> itself is placed in a stage. I show() and hide() the stage as necessary >> (in the setup() / tearDown() methods). Without this, events don't fire. >> 3) I use the separate javafx.scene.control.KeyModifier class to provide >> zero or more keyboard modifiers (shift, alt, ctrl, meta) to the keyboard >> input. An enum might already exist for these modifiers, but I'm not sure... >> 4) I have convenience methods (for my needs, anyway) for >> up/down/left/right keyboard inputs, in the form of >> 'do*ArrowPress(KeyModifier... modifiers)' methods. I'm sure more can be >> added... >> 5) For all other keyboard input, you can use the 'doKeyPress(KeyCode >> keyCode, KeyModifier... modifiers)' method. >> >> Any feedback would be appreciated. >> > From knut.arne.vedaa at broadpark.no Wed Jan 4 04:47:43 2012 From: knut.arne.vedaa at broadpark.no (Knut Arne Vedaa) Date: Wed, 04 Jan 2012 13:47:43 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F03F1E0.5020809@tbee.org> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <4F03F1E0.5020809@tbee.org> Message-ID: <4F044A6F.4030508@broadpark.no> On 04.01.2012 07:29, Tom Eugelink wrote: > Personally I'd flag RT-17932 as "not a bug" and tell the owner of the > issue to start checking isCancelled. That would be me. :) I agree that it is not so much a bug as non-intuitive behaviour, at least to a naive API user (that would also be me). So the best solution might very well be to define the current behaviour as intended behaviour and improve the documentation. Knut Arne From roman at kennke.org Wed Jan 4 05:11:13 2012 From: roman at kennke.org (Roman Kennke) Date: Wed, 04 Jan 2012 14:11:13 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> Message-ID: <1325682673.3443.6.camel@moonlight> Hi Richard, I agree with the other's sentiments that JavaFX should not try to fix faulty code. I would lean to do more or less what is done in java.util.concurrent, especially the FutureTask class. I.e. provide an API to check isCancelled() and otherwise use InterruptionException to correctly interrupt a thread: http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html In my experience, APIs that let people write shitty code leads to a lot of shitty code being written (surprise!), especially when threads are involved. Cheers, Roman Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: > http://javafx-jira.kenai.com/browse/RT-17932 > > I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: > > - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) > - The developer of a Task can check isCancelled from the background thread and self-terminate > - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled > - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! > - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). > > In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. > > If you write a Task, and do nothing smart: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > doSomething(); > } > return null; > } > }; > > In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > doSomething(); > updateProgress(i, 100); > } > return null; > } > }; > > This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. > > In this case...: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) { > Platform.runLater(new Runnable() { > public void run() { > updateMessage("Cancelled") > } > }); > break; > } > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > }; > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > > @Override protected void cancelled() { > updateMessage("Cancelled"); > } > }; > > I think this works fairly well. Anything I'm missing? > > Thanks! > Richard From tom.schindl at bestsolution.at Wed Jan 4 06:12:54 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Wed, 04 Jan 2012 15:12:54 +0100 Subject: Why is the windows runtime/sdk only provided as .exe Message-ID: <4F045E66.2020408@bestsolution.at> Hi, Now that since 2.0.2 (and also 2.1) are redistributeable it might make sense to provide them also as simple ZIP-Files. I find it odd that if I want to package JavaFX with my product that I first have to install something only my system, navigate to the install dir and copy over the stuff my own project directory. Is it BTW possible to have 2.1 and 2.0.2 installed next to each other? I guess not which makes it hard to test with both versions on the same system, which would be made much more easy if provided as simple ZIP-Files. Would you mind providing JavaFX 2.1 binaries (and maybe also > 2.0.2) as a simple zip-File like you do it with 2.1 OS-X ones? Tom -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From martin.sladecek at oracle.com Wed Jan 4 06:21:48 2012 From: martin.sladecek at oracle.com (Martin Sladecek) Date: Wed, 04 Jan 2012 15:21:48 +0100 Subject: BaseObservableList In-Reply-To: <8A651B2F-A393-4819-AF74-26F9F28FCA70@oracle.com> References: <4EE87DC8.80505@oracle.com> <4EEC4897.4090109@oracle.com> <8A651B2F-A393-4819-AF74-26F9F28FCA70@oracle.com> Message-ID: <4F04607C.9090301@oracle.com> OK, so for the 2.1 we will introduce only this: FXCollections.observableList(List list, Callback extractor); FXCollections.observableArrayList(Callback extractor); These lists will fire update notifications if some of the Observables extracted from the elements will change. For this, we will need a new method in ListChangeListener.Change: public boolean wasUpdated() returning false by default (cannot make it abstract now). BaseObservableList + invalidation() methods will be defered to some later release. Any comments? Rich, are you OK with this? -Martin On 12/19/2011 11:31 AM, Michael Heinrichs wrote: > Hi Martin, > > as I said earlier, I do not like this approach, and I am still not > persuaded otherwise. :-) > > I think it is a rather complex implementation. (Ok, I am in an unfair > advantage here, because I have seen the implementation already.) My > feeling is, that you cannot do anything out of ordinary without > knowing the source code of BaseObservableList. And this is always a > bad sign. For example there are two patterns how to generate the > Change object, one for simple operations and one for complex. What is > a simple operation and what is a complex operation? Can an extending > class change the pattern that is used by default for a certain > operation? Why do I have to call commit() after generating a simple > Change and when should I call it? > > There is a lot of functionality in this class that is only needed for > writable lists. I expect almost all extensions of BaseObservableList > will be read-only. It seems to be a waste to have all of that > functionality in a class, if it is most of the time not needed. IMO > BaseObservableList should be optimized for the read-only case and be > extendible for the few writable cases. (Or we introduce a two classes > for both the two cases.) > > If you define a public class that is supposed to be extended, you > commit not only on the public API, but also on the implementation. My > feeling is, we are not at a point yet, where we can commit on the > implementation. > > Overall, I do not think we need this for any of the features that is > scheduled for the 2.1 release. We already discussed a good solution > for http://javafx-jira.kenai.com/browse/RT-15029 internally that does > not require the introduction of BaseObservableList. I would suggest, > we focus on the smaller solution for now, discuss that on the public > alias, and keep experimenting a little more with other options for > BaseObservableList after the 2.1 release. > > - Michael > > > > On 17.12.2011, at 08:45, Martin Sladecek wrote: > >> On 12/17/2011 01:22 AM, Richard Bair wrote: >>> Hi Martin, >>> >>>> Hello, >>>> I'm working on http://javafx-jira.kenai.com/browse/RT-15029 and as >>>> part of this effort, I'm doing a refactoring of ObservableListWrapper. >>>> >>>> So I've created an abstract class BaseObservableList (extends >>>> AbstractList) that could serve as a base class for all >>>> ObservableLists implementations. >>> OK, the "Base" naming convention is one that I really regret, I wish >>> we had just done AbstractFoo. Oh well. The things you end up >>> regretting are often not the things you thought you might :-) But >>> the "Base" convention we're using (hopefully everywhere...) is >>> FooBase rather than BaseFoo, so I guess this should be >>> ObservableListBase. >> >> OK. >>> >>>> For a simple (modifiable) implementation, one would need to >>>> implement following methods: >>>> >>>> public int size() { >>>> } >>>> >>>> public E get(int index) { >>>> } >>>> >>>> protected void doAdd(int index, E element) { >>>> } >>>> >>>> protected E doRemove(int index) { >>>> } >>>> >>>> protected E doSet(int index, E element) { >>>> } >>>> >>>> >>>> >>>> So something like ObservableListWrapper would look like this: >>> Is the proposal also to make ObservableListWrapper public? >> >> Yes. I've added invalidate() methods, we were discussing before, into >> BaseObservableList. So making ObservableListWrapper public would also >> allow calling invalidate(from, to) or invalidate(index) on it. >>> >>>> Now when somebody would want to override something else or extend >>>> the implementation, an IterableChangeBuilder, that's used in >>>> BaseObservableList, needs to be used for this purpose. >>>> >>>> So when creating something by delegating to the methods from the >>>> List, you just need to wrap the block in >>>> changeBuilder.beginComplexChange(); and >>>> changeBuilder.endComplexChange(); >>>> This builds up the change in the calls in between instead of >>>> calling the observers multiple times. >>>> >>>> E.g. setAll method works like this >>>> >>>> public boolean setAll(Collection col) { >>>> changeBuilder.beginComplexChange(); >>>> clear(); //does not call ListChangeListeners >>>> addAll(col); //neither does this >>>> changeBuilder.endComplexChange(); //this calls the >>>> ListChangeListeners using just one Change object >>>> return true; >>>> } >>>> >>>> >>>> On the other hand, if you want to override something simple like >>>> add/remove/set or use just doSet, doAdd and doRemove methods or you >>>> directly manipulate with your data structure, you have to use a >>>> different pattern and use methods from the IterableChangeBuilder >>>> (there are methods like nextAdd, nextRemove, nextSet). And then end >>>> it with commit() call. >>>> >>>> So, e.g. you want to implement some fast clear() (the original >>>> clear in AbstractList iterates over the elements and remove them >>>> one by one, building up the resulting change on the way), you would do: >>>> >>>> public void clear() { >>>> //do the fast clear >>>> changeBuilder.nextRemove(0, listOfRemovedItems); >>>> changeBuilder.commit(); //Calls the ListChangeListeners, but >>>> only if not in ComplexChange block >>>> } >>>> >>>> >>>> Of course, these 2 patterns could be combined and you can do >>>> something like this: >>>> >>>> changeBuilder.beginComplexChange(); >>>> clear(); //no call to observers, as we are in ComplexChange block. >>>> // do some stuff using doAdd or by directly changing my data structures >>>> chnageBuilder.nextAdd(0, x); //I've added something so I need to >>>> report this >>>> changeBuilder.endComplexChange(); //calls the ListChangeListeners >>>> >>>> >>>> What do you think about this API? >>> Why is it called an IterableChangeBuilder? Is there a complete >>> description of the API I can see (maybe attach to the issue?). >> We had a NonIterableChange in com.sun.javafx.collections, which did >> not allow iterating using next(), so this one was for creating >> IterableChanges. But it doesn't make much sense in javafx.collections >> package to call it like this, so maybe we could call it >> ListChangeBuilder? >> >> I don't have any javadoc for that yet, but I'll try to describe it >> briefly: >> (package-private) IterableChangeBuilder(BaseObservableList list); >> // Create a new builder associated with list. This is called by >> BaseObservableList in it's constructor >> >> public void beginComplexChange(); // Begins a complex change, >> building up a change until endComplexChangeIsCalled(). Nested complex >> changes are allowed. >> >> public void endComplexChange(); // If this is a topmost complex >> change, create a Change object a call callObservers on the list. >> >> public void commit(); // If we are not in a complexChange, create an >> Change object and call callObservers >> >> public boolean isEmpty(); // if there's some change stored in the builder >> >> public void nextAdd(int from, int to); // added something on interval >> [from, to) >> >> public void nextPermutation(int from, int to, int[] perm); >> //permutation perm on interval [from, to) >> >> public void nextRemove(int idx, E removed); //single element removed >> from idx >> >> public void nextRemove(int idx, List removed); //multiple elements >> removed from idx. >> >> public void nextReplace(int from, int to, List removed); replaced >> removed with elements on interval [from, to) >> >> public void nextSet(int idx, E old); // changed element "old" on >> index "idx" >> >> public void nextUpdate(int pos); // updated element on position pos >> >> public void reset(); //builder reset >> >> >> Right now, it's tightly coupled with BaseObservableList, calling >> directly it's callObservers method, but I'm going to separate them, >> so it can be used in any implementation, not just the one based on >> BaseObservableList. >> Right now, the only way to use it is to use protected field in >> BaseObservableList. >> >> -Martin >> >>> >>> Thanks! >>> Richard >> > From steve.x.northover at oracle.com Wed Jan 4 07:49:48 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Wed, 04 Jan 2012 10:49:48 -0500 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1325682673.3443.6.camel@moonlight> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> Message-ID: <4F04751C.5060102@oracle.com> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. Steve On 04/01/2012 8:11 AM, Roman Kennke wrote: > Hi Richard, > > I agree with the other's sentiments that JavaFX should not try to fix > faulty code. > > I would lean to do more or less what is done in java.util.concurrent, > especially the FutureTask class. I.e. provide an API to check > isCancelled() and otherwise use InterruptionException to correctly > interrupt a thread: > > http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html > > In my experience, APIs that let people write shitty code leads to a lot > of shitty code being written (surprise!), especially when threads are > involved. > > Cheers, Roman > > Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >> http://javafx-jira.kenai.com/browse/RT-17932 >> >> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >> >> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >> - The developer of a Task can check isCancelled from the background thread and self-terminate >> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >> >> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >> >> If you write a Task, and do nothing smart: >> >> new Task() { >> public Object call() throws Exception { >> for (int i=0; i<100; i++) { >> doSomething(); >> } >> return null; >> } >> }; >> >> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >> >> new Task() { >> public Object call() throws Exception { >> for (int i=0; i<100; i++) { >> doSomething(); >> updateProgress(i, 100); >> } >> return null; >> } >> }; >> >> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >> >> In this case...: >> >> new Task() { >> public Object call() throws Exception { >> for (int i=0; i<100; i++) { >> try { >> doSomething(); >> updateProgress(i, 100); >> } catch (Exception e) { } >> } >> return null; >> } >> }; >> >> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >> >> new Task() { >> public Object call() throws Exception { >> for (int i=0; i<100; i++) { >> if (isCancelled()) break; >> try { >> doSomething(); >> updateProgress(i, 100); >> } catch (Exception e) { } >> } >> return null; >> } >> }; >> >> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >> >> new Task() { >> public Object call() throws Exception { >> for (int i=0; i<100; i++) { >> if (isCancelled()) { >> Platform.runLater(new Runnable() { >> public void run() { >> updateMessage("Cancelled") >> } >> }); >> break; >> } >> try { >> doSomething(); >> updateProgress(i, 100); >> } catch (Exception e) { } >> } >> return null; >> } >> }; >> >> new Task() { >> public Object call() throws Exception { >> for (int i=0; i<100; i++) { >> if (isCancelled()) break; >> try { >> doSomething(); >> updateProgress(i, 100); >> } catch (Exception e) { } >> } >> return null; >> } >> >> @Override protected void cancelled() { >> updateMessage("Cancelled"); >> } >> }; >> >> I think this works fairly well. Anything I'm missing? >> >> Thanks! >> Richard > From kevin.rushforth at oracle.com Wed Jan 4 08:22:39 2012 From: kevin.rushforth at oracle.com (Kevin Rushforth) Date: Wed, 04 Jan 2012 08:22:39 -0800 Subject: Why is the windows runtime/sdk only provided as .exe In-Reply-To: <4F045E66.2020408@bestsolution.at> References: <4F045E66.2020408@bestsolution.at> Message-ID: <4F047CCF.6080600@oracle.com> Good question. The SDK is available as both an installer and a zip, and we could consider make the runtime available as a zip file as well, so please file a JIRA feature request for this. As for your other question, it is not currently possible to have 2.0.2 and 2.1 instaled side-by-side. -- Kevin Tom Schindl wrote: > Hi, > > Now that since 2.0.2 (and also 2.1) are redistributeable it might make > sense to provide them also as simple ZIP-Files. > > I find it odd that if I want to package JavaFX with my product that I > first have to install something only my system, navigate to the install > dir and copy over the stuff my own project directory. > > Is it BTW possible to have 2.1 and 2.0.2 installed next to each other? I > guess not which makes it hard to test with both versions on the same > system, which would be made much more easy if provided as simple ZIP-Files. > > Would you mind providing JavaFX 2.1 binaries (and maybe also > 2.0.2) as > a simple zip-File like you do it with 2.1 OS-X ones? > > Tom > > From tom.schindl at bestsolution.at Wed Jan 4 08:30:24 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Wed, 04 Jan 2012 17:30:24 +0100 Subject: Why is the windows runtime/sdk only provided as .exe In-Reply-To: <4F047CCF.6080600@oracle.com> References: <4F045E66.2020408@bestsolution.at> <4F047CCF.6080600@oracle.com> Message-ID: <4F047EA0.5060300@bestsolution.at> Hi Kevin, Well maybe I'm blind but for win32 the there is NO zip available from this page [1]. The only thing available currently as a zip is the OS-X SDK version which misses the win32 native libs not? I'll file a JIRA. [1]http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html Am 04.01.12 17:22, schrieb Kevin Rushforth: > Good question. The SDK is available as both an installer and a zip, and > we could consider make the runtime available as a zip file as well, so > please file a JIRA feature request for this. > > As for your other question, it is not currently possible to have 2.0.2 > and 2.1 instaled side-by-side. > > -- Kevin > > > Tom Schindl wrote: >> Hi, >> >> Now that since 2.0.2 (and also 2.1) are redistributeable it might make >> sense to provide them also as simple ZIP-Files. >> >> I find it odd that if I want to package JavaFX with my product that I >> first have to install something only my system, navigate to the install >> dir and copy over the stuff my own project directory. >> >> Is it BTW possible to have 2.1 and 2.0.2 installed next to each other? I >> guess not which makes it hard to test with both versions on the same >> system, which would be made much more easy if provided as simple >> ZIP-Files. >> >> Would you mind providing JavaFX 2.1 binaries (and maybe also > 2.0.2) as >> a simple zip-File like you do it with 2.1 OS-X ones? >> >> Tom >> >> -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From kevin.rushforth at oracle.com Wed Jan 4 08:37:39 2012 From: kevin.rushforth at oracle.com (Kevin Rushforth) Date: Wed, 04 Jan 2012 08:37:39 -0800 Subject: Why is the windows runtime/sdk only provided as .exe In-Reply-To: <4F047EA0.5060300@bestsolution.at> References: <4F045E66.2020408@bestsolution.at> <4F047CCF.6080600@oracle.com> <4F047EA0.5060300@bestsolution.at> Message-ID: <4F048053.6010107@oracle.com> Oh, maybe we don't actually release the SDK on the public web page either (I knew we didn't release the runtime as a zip since we don't even generate it internally). -- Kevin Tom Schindl wrote: > Hi Kevin, > > Well maybe I'm blind but for win32 the there is NO zip available from > this page [1]. > > The only thing available currently as a zip is the OS-X SDK version > which misses the win32 native libs not? I'll file a JIRA. > > [1]http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html > > > Am 04.01.12 17:22, schrieb Kevin Rushforth: > >> Good question. The SDK is available as both an installer and a zip, and >> we could consider make the runtime available as a zip file as well, so >> please file a JIRA feature request for this. >> >> As for your other question, it is not currently possible to have 2.0.2 >> and 2.1 instaled side-by-side. >> >> -- Kevin >> >> >> Tom Schindl wrote: >> >>> Hi, >>> >>> Now that since 2.0.2 (and also 2.1) are redistributeable it might make >>> sense to provide them also as simple ZIP-Files. >>> >>> I find it odd that if I want to package JavaFX with my product that I >>> first have to install something only my system, navigate to the install >>> dir and copy over the stuff my own project directory. >>> >>> Is it BTW possible to have 2.1 and 2.0.2 installed next to each other? I >>> guess not which makes it hard to test with both versions on the same >>> system, which would be made much more easy if provided as simple >>> ZIP-Files. >>> >>> Would you mind providing JavaFX 2.1 binaries (and maybe also > 2.0.2) as >>> a simple zip-File like you do it with 2.1 OS-X ones? >>> >>> Tom >>> >>> >>> > > > From Richard.Bair at oracle.com Wed Jan 4 10:55:20 2012 From: Richard.Bair at oracle.com (Richard Bair) Date: Wed, 4 Jan 2012 10:55:20 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F04751C.5060102@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> Message-ID: <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. *

Examples

*

* The following set of examples demonstrate some of the most common uses of * Tasks. *

* *

A Simple Loop

* *

* The first example is a simple loop that does nothing particularly useful, * but demonstrates the fundamental aspects of writing a Task correctly. This * example will simply loop and print to standard out on each loop iteration. * When it completes, it returns the number of times it iterated. *

* *

 *     Task<Integer> task = new Task<Integer>() {
 *         @@Override protected Integer call() throws Exception {
 *             int iterations = 0;
 *             for (iterations = 0; iterations < 1000; iterations++) {
 *                 if (isCancelled()) {
 *                     break;
 *                 }
 *                 System.out.println("Iteration " + iterations);
 *             }
 *             return iterations;
 *         }
 *     }
 * 
* *

* First, we define what type of value is returned from this Task. In this * case, we want to return the number of times we iterated, so we will * specify the Task to be of type Integer by using generics. Then, within * the implementation of the call method, we iterate from * 0 to 1000. On each iteration, we check to see whether this Task has * been cancelled. If it has been, then we break out of the loop and return * the number of times we iterated. Otherwise a message is printed to * the console and the iteration count increased and we continue looping. *

* *

* Checking for isCancelled() in the loop body is critical, otherwise the * developer may cancel the task, but the task will continue running * and updating both the progress and returning the incorrect result * from the end of the call method. A correct implementation * of a Task will always check for cancellation. *

* *

A Simple Loop With Progress Notification

* *

* Similar to the previous example, except this time we will modify the * progress of the Task in each iteration. Note that we have a choice * to make in the case of cancellation. Do we want to set the progress back * to -1 (indeterminate) when the Task is cancelled, or do we want to leave * the progress where it was at? In this case, lets leave the progress alone * and only update the message on cancellation, though updating the * progress after cancellation is a perfectly valid choice. *

* *

 *     Task<Integer> task = new Task<Integer>() {
 *         @@Override protected Integer call() throws Exception {
 *             int iterations = 0;
 *             for (iterations = 0; iterations < 1000; iterations++) {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, 1000);
 *             }
 *             return iterations;
 *         }
 *     }
 * 
* *

* As before, within the for loop we check whether the Task has been * cancelled. If it has been cancelled, we will update the Task's * message to indicate that it has been cancelled, and then break as * before. If the Task has not been cancelled, then we will update its * message to indicate the current iteration and then update the * progress to indicate the current progress. *

* *

A Simple Loop With Progress Notification And Blocking Calls

* *

* This example adds to the previous examples a blocking call. Because a * blocking call may thrown an InterruptedException, and because an * InterruptedException may occur as a result of the Task being cancelled, * we need to be sure to handle the InterruptedException and check on the * cancel state. *

* *

 *     Task<Integer> task = new Task<Integer>() {
 *         @@Override protected Integer call() throws Exception {
 *             int iterations = 0;
 *             for (iterations = 0; iterations < 1000; iterations++) {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, 1000);
 *
 *                 // Now block the thread for a short time, but be sure
 *                 // to check the interrupted exception for cancellation!
 *                 try {
 *                     Thread.sleep(100);
 *                 } catch (InterruptedException interrupted) {
 *                     if (isCancelled()) {
 *                         updateMessage("Cancelled");
 *                         break;
 *                     }
 *                 }
 *             }
 *             return iterations;
 *         }
 *     }
 * 
* *

* Here we have added to the body of the loop a Thread.sleep * call. Since this is a blocking call, I have to handle the potential * InterruptedException. Within the catch block, I will check whether * the Task has been cancelled, and if so, update the message accordingly * and break out of the loop. *

* *

A Task Which Takes Parameters

* *

* Most Tasks require some parameters in order to do useful work. For * example, a DeleteRecordTask needs the object or primary key to delete * from the database. A ReadFileTask needs the URI of the file to be read. * Because Tasks operate on a background thread, care must be taken to * make sure the body of the call method does not read or * modify any shared state. There are two techniques most useful for * doing this: using final variables, and passing variables to a Task * during construction. *

* *

* When using a Task as an anonymous class, the most natural way to pass * parameters to the Task is by using final variables. In this example, * we pass to the Task the total number of times the Task should iterate. *

* *

 *     final int totalIterations = 900;
 *     Task<Integer> task = new Task<Integer>() {
 *         @@Override protected Integer call() throws Exception {
 *             int iterations = 0;
 *             for (iterations = 0; iterations < totalIterations; iterations++) {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, totalIterations);
 *             }
 *             return iterations;
 *         }
 *     }
 * 
* *

* Since totalIterations is final, the call * method can safely read it and refer to it from a background thread. *

* *

* When writing Task libraries (as opposed to specific-use implementations), * we need to use a different technique. In this case, I will create an * IteratingTask which performs the same work as above. This time, since * the IteratingTask is defined in its own file, it will need to have * parameters passed to it in its constructor. These parameters are * assigned to final variables. *

* *

 *     public class IteratingTask extends Task<Integer> {
 *         private final int totalIterations;
 *
 *         public IteratingTask(int totalIterations) {
 *             this.totalIterations = totalIterations;
 *         }
 *
 *         @@Override protected Integer call() throws Exception {
 *             int iterations = 0;
 *             for (iterations = 0; iterations < totalIterations; iterations++) {
 *                 if (isCancelled()) {
 *                     updateMessage("Cancelled");
 *                     break;
 *                 }
 *                 updateMessage("Iteration " + iterations);
 *                 updateProgress(iterations, totalIterations);
 *             }
 *             return iterations;
 *         }
 *     }
 * 
* *

And then when used:

* *

 *     IteratingTask task = new IteratingTask(800);
 * 
* *

In this way, parameters are passed to the IteratingTask in a safe * manner, and again, are final. Thus, the call method can * safely read this state from a background thread.

* *

WARNING: Do not pass mutable state to a Task and then operate on it * from a background thread. Doing so may introduce race conditions. In * particular, suppose you had a SaveCustomerTask which took a Customer * in its constructor. Although the SaveCustomerTask may have a final * reference to the Customer, if the Customer object is mutable, then it * is possible that both the SaveCustomerTask and some other application code * will be reading or modifying the state of the Customer from different * threads. Be very careful in such cases, that while a mutable object such * as this Customer is being used from a background thread, that it is * not being used also from another thread. In particular, if the background * thread is reading data from the database and updating the Customer object, * and the Customer object is bound to scene graph nodes (such as UI * controls), then there could be a violation of threading rules! For such * cases, modify the Customer object from the FX Application Thread rather * than from the background thread.

* *

 *     public class UpdateCustomerTask extends Task<Customer> {
 *         private final Customer customer;
 *
 *         public UpdateCustomerTask(Customer customer) {
 *             this.customer = customer;
 *         }
 *
 *         @@Override protected Customer call() throws Exception {
 *             // pseudo-code:
 *             //   query the database
 *             //   read the values
 *
 *             // Now update the customer
 *             Platform.runLater(new Runnable() {
 *                 @@Override public void run() {
 *                     customer.firstName(rs.getString("FirstName"));
 *                     // etc
 *                 }
 *             });
 *
 *             return customer;
 *         }
 *     }
 * 
* *

A Task Which Returns No Value

* *

* Many, if not most, Tasks should return a value upon completion. For * CRUD Tasks, one would expect that a "Create" Task would return the newly * created object or primary key, a "Read" Task would return the read * object, an "Update" task would return the number of records updated, * and a "Delete" task would return the number of records deleted. *

* *

* However sometimes there just isn't anything truly useful to return. * For example, I might have a Task which writes to a file. Task has built * into it a mechanism for indicating whether it has succeeded or failed * along with the number of bytes written (the progress), and thus there is * nothing really for me to return. In such a case, you can use the Void * type. This is a special type in the Java language which can only be * assigned the value of null. You would use it as follows: *

* *

 *     final String filePath = "/foo.txt";
 *     final String contents = "Some contents";
 *     Task<Void> task = new Task<Void>() {
 *         @@Override protected Void call() throws Exception {
 *             File file = new File(filePath);
 *             FileOutputStream out = new FileOutputStream(file);
 *             // ... and other code to write the contents ...
 *
 *             // Return null at the end of a Task of type Void
 *             return null;
 *         }
 *     }
 * 
* *

A Task Which Returns An ObservableList

* *

Because the ListView, TableView, and other UI controls and scene graph * nodes make use of ObservableList, it is common to want to create and return * an ObservableList from a Task. When you do not care to display intermediate * values, the easiest way to correctly write such a Task is simply to * construct an ObservableList within the call method, and then * return it at the conclusion of the Task.

* *

 *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
 *         @@Override protected ObservableList<Rectangle> call() throws Exception {
 *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
 *             for (int i=0; i<100; i++) {
 *                 if (isCancelled()) break;
 *                 Rectangle r = new Rectangle(10, 10);
 *                 r.setX(10 * i);
 *                 results.add(r);
 *             }
 *             return results;
 *         }
 *     }
 * 
* *

In the above example, we are going to create 100 rectangles and return * them from this task. An ObservableList is created within the * call method, populated, and then returned.

* *

A Task Which Returns Partial Results

* *

Sometimes you want to create a Task which will return partial results. * Perhaps you are building a complex scene graph and want to show the * scene graph as it is being constructed. Or perhaps you are reading a large * amount of data over the network and want to display the entries in a * TableView as the data is arriving. In such cases, there is some shared state * available both to the FX Application Thread and the background thread. * Great care must be taken to never update shared state from any * thread other than the FX Application Thread.

* *

The easiest way to do this is to expose a new property on the Task * which will represent the partial result. Then make sure to use * Platform.runLater when adding new items to the partial * result.

* *

 *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
 *         // Uses Java 7 diamond operator
 *         private ReadOnlyObjectWrapper> partialResults =
 *             new ReadOnlyObjectWrapper<>(this, "partialResults");
 *
 *         public final ObservableList getPartialResults() { return partialResults; }
 *         public final ReadOnlyObjectProperty> partialResultsProperty() {
 *             return partialResults.getReadOnlyProperty();
 *         }
 *
 *         @@Override protected ObservableList call() throws Exception {
 *             for (int i=0; i<100; i++) {
 *                 if (isCancelled()) break;
 *                 final Rectangle r = new Rectangle(10, 10);
 *                 r.setX(10 * i);
 *                 Platform.runLater(new Runnable() {
 *                     @@Override public void run() {
 *                         partialResults.add(r);
 *                     }
 *                 });
 *             }
 *             return partialResults;
 *         }
 *     }
 * 
* * * *

A Task Which Modifies The Scene Graph

* *

Generally, Tasks should not interact directly with the UI. Doing so * creates a tight coupling between a specific Task implementation and a * specific part of your UI. However, when you do want to create such a * coupling, you must ensure that you use Platform.runLater * so that any modifications of the scene graph occur on the * FX Application Thread.

* *

 *     final Group group = new Group();
 *     Task<Void> task = new Task<Void>() {
 *         @@Override protected Void call() throws Exception {
 *             for (int i=0; i<100; i++) {
 *                 if (isCancelled()) break;
 *                 final Rectangle r = new Rectangle(10, 10);
 *                 r.setX(10 * i);
 *                 Platform.runLater(new Runnable() {
 *                     @@Override public void run() {
 *                         group.getChildren().add(r);
 *                     }
 *                 });
 *             }
 *             return null;
 *         }
 *     }
 * 
* *

Reacting To State Changes Generically

* *

Sometimes you may want to write a Task which updates its progress, * message, text, or in some other way reacts whenever a state change * happens on the Task. For example, you may want to change the status * message on the Task on Failure, Success, Running, or Cancelled state changes. *

*

 *     Task<Integer> task = new Task<Integer>() {
 *         @@Override protected Integer call() throws Exception {
 *             int iterations = 0;
 *             for (iterations = 0; iterations < 1000; iterations++) {
 *                 if (isCancelled()) {
 *                     break;
 *                 }
 *                 System.out.println("Iteration " + iterations);
 *             }
 *             return iterations;
 *         }
 *
 *         @@Override protected void succeeded() {
 *             super.succeeded();
 *             updateMessage("Done!");
 *         }
 *
 *         @@Override protected void cancelled() {
 *             super.cancelled();
 *             updateMessage("Cancelled!");
 *         }
 *
 *         @@Override protected void failed() {
 *             super.failed();
 *             updateMessage("Failed!");
 *         }
 *     }
 * 
You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. So the current proposal for this issue is: close as not an issue, and improve the documentation as above. Thanks everybody for your very valuable input! Richard On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: > +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. > > Steve > > On 04/01/2012 8:11 AM, Roman Kennke wrote: >> Hi Richard, >> >> I agree with the other's sentiments that JavaFX should not try to fix >> faulty code. >> >> I would lean to do more or less what is done in java.util.concurrent, >> especially the FutureTask class. I.e. provide an API to check >> isCancelled() and otherwise use InterruptionException to correctly >> interrupt a thread: >> >> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >> >> In my experience, APIs that let people write shitty code leads to a lot >> of shitty code being written (surprise!), especially when threads are >> involved. >> >> Cheers, Roman >> >> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>> http://javafx-jira.kenai.com/browse/RT-17932 >>> >>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>> >>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>> >>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>> >>> If you write a Task, and do nothing smart: >>> >>> new Task() { >>> public Object call() throws Exception { >>> for (int i=0; i<100; i++) { >>> doSomething(); >>> } >>> return null; >>> } >>> }; >>> >>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>> >>> new Task() { >>> public Object call() throws Exception { >>> for (int i=0; i<100; i++) { >>> doSomething(); >>> updateProgress(i, 100); >>> } >>> return null; >>> } >>> }; >>> >>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>> >>> In this case...: >>> >>> new Task() { >>> public Object call() throws Exception { >>> for (int i=0; i<100; i++) { >>> try { >>> doSomething(); >>> updateProgress(i, 100); >>> } catch (Exception e) { } >>> } >>> return null; >>> } >>> }; >>> >>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>> >>> new Task() { >>> public Object call() throws Exception { >>> for (int i=0; i<100; i++) { >>> if (isCancelled()) break; >>> try { >>> doSomething(); >>> updateProgress(i, 100); >>> } catch (Exception e) { } >>> } >>> return null; >>> } >>> }; >>> >>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>> >>> new Task() { >>> public Object call() throws Exception { >>> for (int i=0; i<100; i++) { >>> if (isCancelled()) { >>> Platform.runLater(new Runnable() { >>> public void run() { >>> updateMessage("Cancelled") >>> } >>> }); >>> break; >>> } >>> try { >>> doSomething(); >>> updateProgress(i, 100); >>> } catch (Exception e) { } >>> } >>> return null; >>> } >>> }; >>> >>> new Task() { >>> public Object call() throws Exception { >>> for (int i=0; i<100; i++) { >>> if (isCancelled()) break; >>> try { >>> doSomething(); >>> updateProgress(i, 100); >>> } catch (Exception e) { } >>> } >>> return null; >>> } >>> >>> @Override protected void cancelled() { >>> updateMessage("Cancelled"); >>> } >>> }; >>> >>> I think this works fairly well. Anything I'm missing? >>> >>> Thanks! >>> Richard >> From steve.x.northover at oracle.com Wed Jan 4 10:59:45 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Wed, 04 Jan 2012 13:59:45 -0500 Subject: Review of solution for Cancelled Tasks In-Reply-To: <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> Message-ID: <4F04A1A1.6050503@oracle.com> What happens now when updateProgress() and friends are called for a canceled task? Steve On 04/01/2012 1:55 PM, Richard Bair wrote: > OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. > > My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). > > I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. > > *

Examples

> *

> * The following set of examples demonstrate some of the most common uses of > * Tasks. > *

> * > *

A Simple Loop

> * > *

> * The first example is a simple loop that does nothing particularly useful, > * but demonstrates the fundamental aspects of writing a Task correctly. This > * example will simply loop and print to standard out on each loop iteration. > * When it completes, it returns the number of times it iterated. > *

> * > *

>   *     Task<Integer> task = new Task<Integer>() {
>   *         @@Override protected Integer call() throws Exception {
>   *             int iterations = 0;
>   *             for (iterations = 0; iterations< 1000; iterations++) {
>   *                 if (isCancelled()) {
>   *                     break;
>   *                 }
>   *                 System.out.println("Iteration " + iterations);
>   *             }
>   *             return iterations;
>   *         }
>   *     }
>   *
> * > *

> * First, we define what type of value is returned from this Task. In this > * case, we want to return the number of times we iterated, so we will > * specify the Task to be of type Integer by using generics. Then, within > * the implementation of thecall method, we iterate from > * 0 to 1000. On each iteration, we check to see whether this Task has > * been cancelled. If it has been, then we break out of the loop and return > * the number of times we iterated. Otherwise a message is printed to > * the console and the iteration count increased and we continue looping. > *

> * > *

> * Checking for isCancelled() in the loop body is critical, otherwise the > * developer may cancel the task, but the task will continue running > * and updating both the progress and returning the incorrect result > * from the end of thecall method. A correct implementation > * of a Task will always check for cancellation. > *

> * > *

A Simple Loop With Progress Notification

> * > *

> * Similar to the previous example, except this time we will modify the > * progress of the Task in each iteration. Note that we have a choice > * to make in the case of cancellation. Do we want to set the progress back > * to -1 (indeterminate) when the Task is cancelled, or do we want to leave > * the progress where it was at? In this case, lets leave the progress alone > * and only update the message on cancellation, though updating the > * progress after cancellation is a perfectly valid choice. > *

> * > *

>   *     Task<Integer> task = new Task<Integer>() {
>   *         @@Override protected Integer call() throws Exception {
>   *             int iterations = 0;
>   *             for (iterations = 0; iterations< 1000; iterations++) {
>   *                 if (isCancelled()) {
>   *                     updateMessage("Cancelled");
>   *                     break;
>   *                 }
>   *                 updateMessage("Iteration " + iterations);
>   *                 (iterations, 1000);
>   *             }
>   *             return iterations;
>   *         }
>   *     }
>   *
> * > *

> * As before, within the for loop we check whether the Task has been > * cancelled. If it has been cancelled, we will update the Task's > * message to indicate that it has been cancelled, and then break as > * before. If the Task has not been cancelled, then we will update its > * message to indicate the current iteration and then update the > * progress to indicate the current progress. > *

> * > *

A Simple Loop With Progress Notification And Blocking Calls

> * > *

> * This example adds to the previous examples a blocking call. Because a > * blocking call may thrown an InterruptedException, and because an > * InterruptedException may occur as a result of the Task being cancelled, > * we need to be sure to handle the InterruptedException and check on the > * cancel state. > *

> * > *

>   *     Task<Integer> task = new Task<Integer>() {
>   *         @@Override protected Integer call() throws Exception {
>   *             int iterations = 0;
>   *             for (iterations = 0; iterations< 1000; iterations++) {
>   *                 if (isCancelled()) {
>   *                     updateMessage("Cancelled");
>   *                     break;
>   *                 }
>   *                 updateMessage("Iteration " + iterations);
>   *                 updateProgress(iterations, 1000);
>   *
>   *                 // Now block the thread for a short time, but be sure
>   *                 // to check the interrupted exception for cancellation!
>   *                 try {
>   *                     Thread.sleep(100);
>   *                 } catch (InterruptedException interrupted) {
>   *                     if (isCancelled()) {
>   *                         updateMessage("Cancelled");
>   *                         break;
>   *                     }
>   *                 }
>   *             }
>   *             return iterations;
>   *         }
>   *     }
>   *
> * > *

> * Here we have added to the body of the loop aThread.sleep > * call. Since this is a blocking call, I have to handle the potential > * InterruptedException. Within the catch block, I will check whether > * the Task has been cancelled, and if so, update the message accordingly > * and break out of the loop. > *

> * > *

A Task Which Takes Parameters

> * > *

> * Most Tasks require some parameters in order to do useful work. For > * example, a DeleteRecordTask needs the object or primary key to delete > * from the database. A ReadFileTask needs the URI of the file to be read. > * Because Tasks operate on a background thread, care must be taken to > * make sure the body of thecall method does not read or > * modify any shared state. There are two techniques most useful for > * doing this: using final variables, and passing variables to a Task > * during construction. > *

> * > *

> * When using a Task as an anonymous class, the most natural way to pass > * parameters to the Task is by using final variables. In this example, > * we pass to the Task the total number of times the Task should iterate. > *

> * > *

>   *     final int totalIterations = 900;
>   *     Task<Integer> task = new Task<Integer>() {
>   *         @@Override protected Integer call() throws Exception {
>   *             int iterations = 0;
>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>   *                 if (isCancelled()) {
>   *                     updateMessage("Cancelled");
>   *                     break;
>   *                 }
>   *                 updateMessage("Iteration " + iterations);
>   *                 updateProgress(iterations, totalIterations);
>   *             }
>   *             return iterations;
>   *         }
>   *     }
>   *
> * > *

> * SincetotalIterations is final, thecall > * method can safely read it and refer to it from a background thread. > *

> * > *

> * When writing Task libraries (as opposed to specific-use implementations), > * we need to use a different technique. In this case, I will create an > * IteratingTask which performs the same work as above. This time, since > * the IteratingTask is defined in its own file, it will need to have > * parameters passed to it in its constructor. These parameters are > * assigned to final variables. > *

> * > *

>   *     public class IteratingTask extends Task<Integer> {
>   *         private final int totalIterations;
>   *
>   *         public IteratingTask(int totalIterations) {
>   *             this.totalIterations = totalIterations;
>   *         }
>   *
>   *         @@Override protected Integer call() throws Exception {
>   *             int iterations = 0;
>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>   *                 if (isCancelled()) {
>   *                     updateMessage("Cancelled");
>   *                     break;
>   *                 }
>   *                 updateMessage("Iteration " + iterations);
>   *                 updateProgress(iterations, totalIterations);
>   *             }
>   *             return iterations;
>   *         }
>   *     }
>   *
> * > *

And then when used:

> * > *

>   *     IteratingTask task = new IteratingTask(800);
>   *
> * > *

In this way, parameters are passed to the IteratingTask in a safe > * manner, and again, are final. Thus, thecall method can > * safely read this state from a background thread.

> * > *

WARNING: Do not pass mutable state to a Task and then operate on it > * from a background thread. Doing so may introduce race conditions. In > * particular, suppose you had a SaveCustomerTask which took a Customer > * in its constructor. Although the SaveCustomerTask may have a final > * reference to the Customer, if the Customer object is mutable, then it > * is possible that both the SaveCustomerTask and some other application code > * will be reading or modifying the state of the Customer from different > * threads. Be very careful in such cases, that while a mutable object such > * as this Customer is being used from a background thread, that it is > * not being used also from another thread. In particular, if the background > * thread is reading data from the database and updating the Customer object, > * and the Customer object is bound to scene graph nodes (such as UI > * controls), then there could be a violation of threading rules! For such > * cases, modify the Customer object from the FX Application Thread rather > * than from the background thread.

> * > *

>   *     public class UpdateCustomerTask extends Task<Customer> {
>   *         private final Customer customer;
>   *
>   *         public UpdateCustomerTask(Customer customer) {
>   *             this.customer = customer;
>   *         }
>   *
>   *         @@Override protected Customer call() throws Exception {
>   *             // pseudo-code:
>   *             //   query the database
>   *             //   read the values
>   *
>   *             // Now update the customer
>   *             Platform.runLater(new Runnable() {
>   *                 @@Override public void run() {
>   *                     customer.firstName(rs.getString("FirstName"));
>   *                     // etc
>   *                 }
>   *             });
>   *
>   *             return customer;
>   *         }
>   *     }
>   *
> * > *

A Task Which Returns No Value

> * > *

> * Many, if not most, Tasks should return a value upon completion. For > * CRUD Tasks, one would expect that a "Create" Task would return the newly > * created object or primary key, a "Read" Task would return the read > * object, an "Update" task would return the number of records updated, > * and a "Delete" task would return the number of records deleted. > *

> * > *

> * However sometimes there just isn't anything truly useful to return. > * For example, I might have a Task which writes to a file. Task has built > * into it a mechanism for indicating whether it has succeeded or failed > * along with the number of bytes written (the progress), and thus there is > * nothing really for me to return. In such a case, you can use the Void > * type. This is a special type in the Java language which can only be > * assigned the value ofnull. You would use it as follows: > *

> * > *

>   *     final String filePath = "/foo.txt";
>   *     final String contents = "Some contents";
>   *     Task<Void> task = new Task<Void>() {
>   *         @@Override protected Void call() throws Exception {
>   *             File file = new File(filePath);
>   *             FileOutputStream out = new FileOutputStream(file);
>   *             // ... and other code to write the contents ...
>   *
>   *             // Return null at the end of a Task of type Void
>   *             return null;
>   *         }
>   *     }
>   *
> * > *

A Task Which Returns An ObservableList

> * > *

Because the ListView, TableView, and other UI controls and scene graph > * nodes make use of ObservableList, it is common to want to create and return > * an ObservableList from a Task. When you do not care to display intermediate > * values, the easiest way to correctly write such a Task is simply to > * construct an ObservableList within thecall method, and then > * return it at the conclusion of the Task.

> * > *

>   *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>   *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>   *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>   *             for (int i=0; i<100; i++) {
>   *                 if (isCancelled()) break;
>   *                 Rectangle r = new Rectangle(10, 10);
>   *                 r.setX(10 * i);
>   *                 results.add(r);
>   *             }
>   *             return results;
>   *         }
>   *     }
>   *
> * > *

In the above example, we are going to create 100 rectangles and return > * them from this task. An ObservableList is created within the > *call method, populated, and then returned.

> * > *

A Task Which Returns Partial Results

> * > *

Sometimes you want to create a Task which will return partial results. > * Perhaps you are building a complex scene graph and want to show the > * scene graph as it is being constructed. Or perhaps you are reading a large > * amount of data over the network and want to display the entries in a > * TableView as the data is arriving. In such cases, there is some shared state > * available both to the FX Application Thread and the background thread. > * Great care must be taken tonever update shared state from any > * thread other than the FX Application Thread.

> * > *

The easiest way to do this is to expose a new property on the Task > * which will represent the partial result. Then make sure to use > *Platform.runLater when adding new items to the partial > * result.

> * > *

>   *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>   *         // Uses Java 7 diamond operator
>   *         private ReadOnlyObjectWrapper>  partialResults =
>   *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>   *
>   *         public final ObservableList  getPartialResults() { return partialResults; }
>   *         public final ReadOnlyObjectProperty>  partialResultsProperty() {
>   *             return partialResults.getReadOnlyProperty();
>   *         }
>   *
>   *         @@Override protected ObservableList  call() throws Exception {
>   *             for (int i=0; i<100; i++) {
>   *                 if (isCancelled()) break;
>   *                 final Rectangle r = new Rectangle(10, 10);
>   *                 r.setX(10 * i);
>   *                 Platform.runLater(new Runnable() {
>   *                     @@Override public void run() {
>   *                         partialResults.add(r);
>   *                     }
>   *                 });
>   *             }
>   *             return partialResults;
>   *         }
>   *     }
>   *
> * > * > * > *

A Task Which Modifies The Scene Graph

> * > *

Generally, Tasks should not interact directly with the UI. Doing so > * creates a tight coupling between a specific Task implementation and a > * specific part of your UI. However, when you do want to create such a > * coupling, you must ensure that you usePlatform.runLater > * so that any modifications of the scene graph occur on the > * FX Application Thread.

> * > *

>   *     final Group group = new Group();
>   *     Task<Void> task = new Task<Void>() {
>   *         @@Override protected Void call() throws Exception {
>   *             for (int i=0; i<100; i++) {
>   *                 if (isCancelled()) break;
>   *                 final Rectangle r = new Rectangle(10, 10);
>   *                 r.setX(10 * i);
>   *                 Platform.runLater(new Runnable() {
>   *                     @@Override public void run() {
>   *                         group.getChildren().add(r);
>   *                     }
>   *                 });
>   *             }
>   *             return null;
>   *         }
>   *     }
>   *
> * > *

Reacting To State Changes Generically

> * > *

Sometimes you may want to write a Task which updates its progress, > * message, text, or in some other way reacts whenever a state change > * happens on the Task. For example, you may want to change the status > * message on the Task on Failure, Success, Running, or Cancelled state changes. > *

> *

>   *     Task<Integer> task = new Task<Integer>() {
>   *         @@Override protected Integer call() throws Exception {
>   *             int iterations = 0;
>   *             for (iterations = 0; iterations< 1000; iterations++) {
>   *                 if (isCancelled()) {
>   *                     break;
>   *                 }
>   *                 System.out.println("Iteration " + iterations);
>   *             }
>   *             return iterations;
>   *         }
>   *
>   *         @@Override protected void succeeded() {
>   *             super.succeeded();
>   *             updateMessage("Done!");
>   *         }
>   *
>   *         @@Override protected void cancelled() {
>   *             super.cancelled();
>   *             updateMessage("Cancelled!");
>   *         }
>   *
>   *         @@Override protected void failed() {
>   *             super.failed();
>   *             updateMessage("Failed!");
>   *         }
>   *     }
>   *
> > You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. > > So the current proposal for this issue is: close as not an issue, and improve the documentation as above. > > Thanks everybody for your very valuable input! > Richard > > On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: > >> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >> >> Steve >> >> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>> Hi Richard, >>> >>> I agree with the other's sentiments that JavaFX should not try to fix >>> faulty code. >>> >>> I would lean to do more or less what is done in java.util.concurrent, >>> especially the FutureTask class. I.e. provide an API to check >>> isCancelled() and otherwise use InterruptionException to correctly >>> interrupt a thread: >>> >>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>> >>> In my experience, APIs that let people write shitty code leads to a lot >>> of shitty code being written (surprise!), especially when threads are >>> involved. >>> >>> Cheers, Roman >>> >>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>> >>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>> >>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>> >>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>> >>>> If you write a Task, and do nothing smart: >>>> >>>> new Task() { >>>> public Object call() throws Exception { >>>> for (int i=0; i<100; i++) { >>>> doSomething(); >>>> } >>>> return null; >>>> } >>>> }; >>>> >>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>> >>>> new Task() { >>>> public Object call() throws Exception { >>>> for (int i=0; i<100; i++) { >>>> doSomething(); >>>> updateProgress(i, 100); >>>> } >>>> return null; >>>> } >>>> }; >>>> >>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>> >>>> In this case...: >>>> >>>> new Task() { >>>> public Object call() throws Exception { >>>> for (int i=0; i<100; i++) { >>>> try { >>>> doSomething(); >>>> updateProgress(i, 100); >>>> } catch (Exception e) { } >>>> } >>>> return null; >>>> } >>>> }; >>>> >>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>> >>>> new Task() { >>>> public Object call() throws Exception { >>>> for (int i=0; i<100; i++) { >>>> if (isCancelled()) break; >>>> try { >>>> doSomething(); >>>> updateProgress(i, 100); >>>> } catch (Exception e) { } >>>> } >>>> return null; >>>> } >>>> }; >>>> >>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>> >>>> new Task() { >>>> public Object call() throws Exception { >>>> for (int i=0; i<100; i++) { >>>> if (isCancelled()) { >>>> Platform.runLater(new Runnable() { >>>> public void run() { >>>> updateMessage("Cancelled") >>>> } >>>> }); >>>> break; >>>> } >>>> try { >>>> doSomething(); >>>> updateProgress(i, 100); >>>> } catch (Exception e) { } >>>> } >>>> return null; >>>> } >>>> }; >>>> >>>> new Task() { >>>> public Object call() throws Exception { >>>> for (int i=0; i<100; i++) { >>>> if (isCancelled()) break; >>>> try { >>>> doSomething(); >>>> updateProgress(i, 100); >>>> } catch (Exception e) { } >>>> } >>>> return null; >>>> } >>>> >>>> @Override protected void cancelled() { >>>> updateMessage("Cancelled"); >>>> } >>>> }; >>>> >>>> I think this works fairly well. Anything I'm missing? >>>> >>>> Thanks! >>>> Richard From Richard.Bair at oracle.com Wed Jan 4 11:00:58 2012 From: Richard.Bair at oracle.com (Richard Bair) Date: Wed, 4 Jan 2012 11:00:58 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F04A1A1.6050503@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> Message-ID: <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> Today, they just work (they update the progress, title, message). Richard On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: > What happens now when updateProgress() and friends are called for a canceled task? > > Steve > > On 04/01/2012 1:55 PM, Richard Bair wrote: >> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >> >> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >> >> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >> >> *

Examples

>> *

>> * The following set of examples demonstrate some of the most common uses of >> * Tasks. >> *

>> * >> *

A Simple Loop

>> * >> *

>> * The first example is a simple loop that does nothing particularly useful, >> * but demonstrates the fundamental aspects of writing a Task correctly. This >> * example will simply loop and print to standard out on each loop iteration. >> * When it completes, it returns the number of times it iterated. >> *

>> * >> *

>>  *     Task<Integer> task = new Task<Integer>() {
>>  *         @@Override protected Integer call() throws Exception {
>>  *             int iterations = 0;
>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>  *                 if (isCancelled()) {
>>  *                     break;
>>  *                 }
>>  *                 System.out.println("Iteration " + iterations);
>>  *             }
>>  *             return iterations;
>>  *         }
>>  *     }
>>  *
>> * >> *

>> * First, we define what type of value is returned from this Task. In this >> * case, we want to return the number of times we iterated, so we will >> * specify the Task to be of type Integer by using generics. Then, within >> * the implementation of thecall method, we iterate from >> * 0 to 1000. On each iteration, we check to see whether this Task has >> * been cancelled. If it has been, then we break out of the loop and return >> * the number of times we iterated. Otherwise a message is printed to >> * the console and the iteration count increased and we continue looping. >> *

>> * >> *

>> * Checking for isCancelled() in the loop body is critical, otherwise the >> * developer may cancel the task, but the task will continue running >> * and updating both the progress and returning the incorrect result >> * from the end of thecall method. A correct implementation >> * of a Task will always check for cancellation. >> *

>> * >> *

A Simple Loop With Progress Notification

>> * >> *

>> * Similar to the previous example, except this time we will modify the >> * progress of the Task in each iteration. Note that we have a choice >> * to make in the case of cancellation. Do we want to set the progress back >> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >> * the progress where it was at? In this case, lets leave the progress alone >> * and only update the message on cancellation, though updating the >> * progress after cancellation is a perfectly valid choice. >> *

>> * >> *

>>  *     Task<Integer> task = new Task<Integer>() {
>>  *         @@Override protected Integer call() throws Exception {
>>  *             int iterations = 0;
>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>  *                 if (isCancelled()) {
>>  *                     updateMessage("Cancelled");
>>  *                     break;
>>  *                 }
>>  *                 updateMessage("Iteration " + iterations);
>>  *                 (iterations, 1000);
>>  *             }
>>  *             return iterations;
>>  *         }
>>  *     }
>>  *
>> * >> *

>> * As before, within the for loop we check whether the Task has been >> * cancelled. If it has been cancelled, we will update the Task's >> * message to indicate that it has been cancelled, and then break as >> * before. If the Task has not been cancelled, then we will update its >> * message to indicate the current iteration and then update the >> * progress to indicate the current progress. >> *

>> * >> *

A Simple Loop With Progress Notification And Blocking Calls

>> * >> *

>> * This example adds to the previous examples a blocking call. Because a >> * blocking call may thrown an InterruptedException, and because an >> * InterruptedException may occur as a result of the Task being cancelled, >> * we need to be sure to handle the InterruptedException and check on the >> * cancel state. >> *

>> * >> *

>>  *     Task<Integer> task = new Task<Integer>() {
>>  *         @@Override protected Integer call() throws Exception {
>>  *             int iterations = 0;
>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>  *                 if (isCancelled()) {
>>  *                     updateMessage("Cancelled");
>>  *                     break;
>>  *                 }
>>  *                 updateMessage("Iteration " + iterations);
>>  *                 updateProgress(iterations, 1000);
>>  *
>>  *                 // Now block the thread for a short time, but be sure
>>  *                 // to check the interrupted exception for cancellation!
>>  *                 try {
>>  *                     Thread.sleep(100);
>>  *                 } catch (InterruptedException interrupted) {
>>  *                     if (isCancelled()) {
>>  *                         updateMessage("Cancelled");
>>  *                         break;
>>  *                     }
>>  *                 }
>>  *             }
>>  *             return iterations;
>>  *         }
>>  *     }
>>  *
>> * >> *

>> * Here we have added to the body of the loop aThread.sleep >> * call. Since this is a blocking call, I have to handle the potential >> * InterruptedException. Within the catch block, I will check whether >> * the Task has been cancelled, and if so, update the message accordingly >> * and break out of the loop. >> *

>> * >> *

A Task Which Takes Parameters

>> * >> *

>> * Most Tasks require some parameters in order to do useful work. For >> * example, a DeleteRecordTask needs the object or primary key to delete >> * from the database. A ReadFileTask needs the URI of the file to be read. >> * Because Tasks operate on a background thread, care must be taken to >> * make sure the body of thecall method does not read or >> * modify any shared state. There are two techniques most useful for >> * doing this: using final variables, and passing variables to a Task >> * during construction. >> *

>> * >> *

>> * When using a Task as an anonymous class, the most natural way to pass >> * parameters to the Task is by using final variables. In this example, >> * we pass to the Task the total number of times the Task should iterate. >> *

>> * >> *

>>  *     final int totalIterations = 900;
>>  *     Task<Integer> task = new Task<Integer>() {
>>  *         @@Override protected Integer call() throws Exception {
>>  *             int iterations = 0;
>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>  *                 if (isCancelled()) {
>>  *                     updateMessage("Cancelled");
>>  *                     break;
>>  *                 }
>>  *                 updateMessage("Iteration " + iterations);
>>  *                 updateProgress(iterations, totalIterations);
>>  *             }
>>  *             return iterations;
>>  *         }
>>  *     }
>>  *
>> * >> *

>> * SincetotalIterations is final, thecall >> * method can safely read it and refer to it from a background thread. >> *

>> * >> *

>> * When writing Task libraries (as opposed to specific-use implementations), >> * we need to use a different technique. In this case, I will create an >> * IteratingTask which performs the same work as above. This time, since >> * the IteratingTask is defined in its own file, it will need to have >> * parameters passed to it in its constructor. These parameters are >> * assigned to final variables. >> *

>> * >> *

>>  *     public class IteratingTask extends Task<Integer> {
>>  *         private final int totalIterations;
>>  *
>>  *         public IteratingTask(int totalIterations) {
>>  *             this.totalIterations = totalIterations;
>>  *         }
>>  *
>>  *         @@Override protected Integer call() throws Exception {
>>  *             int iterations = 0;
>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>  *                 if (isCancelled()) {
>>  *                     updateMessage("Cancelled");
>>  *                     break;
>>  *                 }
>>  *                 updateMessage("Iteration " + iterations);
>>  *                 updateProgress(iterations, totalIterations);
>>  *             }
>>  *             return iterations;
>>  *         }
>>  *     }
>>  *
>> * >> *

And then when used:

>> * >> *

>>  *     IteratingTask task = new IteratingTask(800);
>>  *
>> * >> *

In this way, parameters are passed to the IteratingTask in a safe >> * manner, and again, are final. Thus, thecall method can >> * safely read this state from a background thread.

>> * >> *

WARNING: Do not pass mutable state to a Task and then operate on it >> * from a background thread. Doing so may introduce race conditions. In >> * particular, suppose you had a SaveCustomerTask which took a Customer >> * in its constructor. Although the SaveCustomerTask may have a final >> * reference to the Customer, if the Customer object is mutable, then it >> * is possible that both the SaveCustomerTask and some other application code >> * will be reading or modifying the state of the Customer from different >> * threads. Be very careful in such cases, that while a mutable object such >> * as this Customer is being used from a background thread, that it is >> * not being used also from another thread. In particular, if the background >> * thread is reading data from the database and updating the Customer object, >> * and the Customer object is bound to scene graph nodes (such as UI >> * controls), then there could be a violation of threading rules! For such >> * cases, modify the Customer object from the FX Application Thread rather >> * than from the background thread.

>> * >> *

>>  *     public class UpdateCustomerTask extends Task<Customer> {
>>  *         private final Customer customer;
>>  *
>>  *         public UpdateCustomerTask(Customer customer) {
>>  *             this.customer = customer;
>>  *         }
>>  *
>>  *         @@Override protected Customer call() throws Exception {
>>  *             // pseudo-code:
>>  *             //   query the database
>>  *             //   read the values
>>  *
>>  *             // Now update the customer
>>  *             Platform.runLater(new Runnable() {
>>  *                 @@Override public void run() {
>>  *                     customer.firstName(rs.getString("FirstName"));
>>  *                     // etc
>>  *                 }
>>  *             });
>>  *
>>  *             return customer;
>>  *         }
>>  *     }
>>  *
>> * >> *

A Task Which Returns No Value

>> * >> *

>> * Many, if not most, Tasks should return a value upon completion. For >> * CRUD Tasks, one would expect that a "Create" Task would return the newly >> * created object or primary key, a "Read" Task would return the read >> * object, an "Update" task would return the number of records updated, >> * and a "Delete" task would return the number of records deleted. >> *

>> * >> *

>> * However sometimes there just isn't anything truly useful to return. >> * For example, I might have a Task which writes to a file. Task has built >> * into it a mechanism for indicating whether it has succeeded or failed >> * along with the number of bytes written (the progress), and thus there is >> * nothing really for me to return. In such a case, you can use the Void >> * type. This is a special type in the Java language which can only be >> * assigned the value ofnull. You would use it as follows: >> *

>> * >> *

>>  *     final String filePath = "/foo.txt";
>>  *     final String contents = "Some contents";
>>  *     Task<Void> task = new Task<Void>() {
>>  *         @@Override protected Void call() throws Exception {
>>  *             File file = new File(filePath);
>>  *             FileOutputStream out = new FileOutputStream(file);
>>  *             // ... and other code to write the contents ...
>>  *
>>  *             // Return null at the end of a Task of type Void
>>  *             return null;
>>  *         }
>>  *     }
>>  *
>> * >> *

A Task Which Returns An ObservableList

>> * >> *

Because the ListView, TableView, and other UI controls and scene graph >> * nodes make use of ObservableList, it is common to want to create and return >> * an ObservableList from a Task. When you do not care to display intermediate >> * values, the easiest way to correctly write such a Task is simply to >> * construct an ObservableList within thecall method, and then >> * return it at the conclusion of the Task.

>> * >> *

>>  *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>  *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>  *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>  *             for (int i=0; i<100; i++) {
>>  *                 if (isCancelled()) break;
>>  *                 Rectangle r = new Rectangle(10, 10);
>>  *                 r.setX(10 * i);
>>  *                 results.add(r);
>>  *             }
>>  *             return results;
>>  *         }
>>  *     }
>>  *
>> * >> *

In the above example, we are going to create 100 rectangles and return >> * them from this task. An ObservableList is created within the >> *call method, populated, and then returned.

>> * >> *

A Task Which Returns Partial Results

>> * >> *

Sometimes you want to create a Task which will return partial results. >> * Perhaps you are building a complex scene graph and want to show the >> * scene graph as it is being constructed. Or perhaps you are reading a large >> * amount of data over the network and want to display the entries in a >> * TableView as the data is arriving. In such cases, there is some shared state >> * available both to the FX Application Thread and the background thread. >> * Great care must be taken tonever update shared state from any >> * thread other than the FX Application Thread.

>> * >> *

The easiest way to do this is to expose a new property on the Task >> * which will represent the partial result. Then make sure to use >> *Platform.runLater when adding new items to the partial >> * result.

>> * >> *

>>  *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>  *         // Uses Java 7 diamond operator
>>  *         private ReadOnlyObjectWrapper>  partialResults =
>>  *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>  *
>>  *         public final ObservableList  getPartialResults() { return partialResults; }
>>  *         public final ReadOnlyObjectProperty>  partialResultsProperty() {
>>  *             return partialResults.getReadOnlyProperty();
>>  *         }
>>  *
>>  *         @@Override protected ObservableList  call() throws Exception {
>>  *             for (int i=0; i<100; i++) {
>>  *                 if (isCancelled()) break;
>>  *                 final Rectangle r = new Rectangle(10, 10);
>>  *                 r.setX(10 * i);
>>  *                 Platform.runLater(new Runnable() {
>>  *                     @@Override public void run() {
>>  *                         partialResults.add(r);
>>  *                     }
>>  *                 });
>>  *             }
>>  *             return partialResults;
>>  *         }
>>  *     }
>>  *
>> * >> * >> * >> *

A Task Which Modifies The Scene Graph

>> * >> *

Generally, Tasks should not interact directly with the UI. Doing so >> * creates a tight coupling between a specific Task implementation and a >> * specific part of your UI. However, when you do want to create such a >> * coupling, you must ensure that you usePlatform.runLater >> * so that any modifications of the scene graph occur on the >> * FX Application Thread.

>> * >> *

>>  *     final Group group = new Group();
>>  *     Task<Void> task = new Task<Void>() {
>>  *         @@Override protected Void call() throws Exception {
>>  *             for (int i=0; i<100; i++) {
>>  *                 if (isCancelled()) break;
>>  *                 final Rectangle r = new Rectangle(10, 10);
>>  *                 r.setX(10 * i);
>>  *                 Platform.runLater(new Runnable() {
>>  *                     @@Override public void run() {
>>  *                         group.getChildren().add(r);
>>  *                     }
>>  *                 });
>>  *             }
>>  *             return null;
>>  *         }
>>  *     }
>>  *
>> * >> *

Reacting To State Changes Generically

>> * >> *

Sometimes you may want to write a Task which updates its progress, >> * message, text, or in some other way reacts whenever a state change >> * happens on the Task. For example, you may want to change the status >> * message on the Task on Failure, Success, Running, or Cancelled state changes. >> *

>> *

>>  *     Task<Integer> task = new Task<Integer>() {
>>  *         @@Override protected Integer call() throws Exception {
>>  *             int iterations = 0;
>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>  *                 if (isCancelled()) {
>>  *                     break;
>>  *                 }
>>  *                 System.out.println("Iteration " + iterations);
>>  *             }
>>  *             return iterations;
>>  *         }
>>  *
>>  *         @@Override protected void succeeded() {
>>  *             super.succeeded();
>>  *             updateMessage("Done!");
>>  *         }
>>  *
>>  *         @@Override protected void cancelled() {
>>  *             super.cancelled();
>>  *             updateMessage("Cancelled!");
>>  *         }
>>  *
>>  *         @@Override protected void failed() {
>>  *             super.failed();
>>  *             updateMessage("Failed!");
>>  *         }
>>  *     }
>>  *
>> >> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >> >> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >> >> Thanks everybody for your very valuable input! >> Richard >> >> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >> >>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>> >>> Steve >>> >>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>> Hi Richard, >>>> >>>> I agree with the other's sentiments that JavaFX should not try to fix >>>> faulty code. >>>> >>>> I would lean to do more or less what is done in java.util.concurrent, >>>> especially the FutureTask class. I.e. provide an API to check >>>> isCancelled() and otherwise use InterruptionException to correctly >>>> interrupt a thread: >>>> >>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>> >>>> In my experience, APIs that let people write shitty code leads to a lot >>>> of shitty code being written (surprise!), especially when threads are >>>> involved. >>>> >>>> Cheers, Roman >>>> >>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>> >>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>> >>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>> >>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>> >>>>> If you write a Task, and do nothing smart: >>>>> >>>>> new Task() { >>>>> public Object call() throws Exception { >>>>> for (int i=0; i<100; i++) { >>>>> doSomething(); >>>>> } >>>>> return null; >>>>> } >>>>> }; >>>>> >>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>> >>>>> new Task() { >>>>> public Object call() throws Exception { >>>>> for (int i=0; i<100; i++) { >>>>> doSomething(); >>>>> updateProgress(i, 100); >>>>> } >>>>> return null; >>>>> } >>>>> }; >>>>> >>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>> >>>>> In this case...: >>>>> >>>>> new Task() { >>>>> public Object call() throws Exception { >>>>> for (int i=0; i<100; i++) { >>>>> try { >>>>> doSomething(); >>>>> updateProgress(i, 100); >>>>> } catch (Exception e) { } >>>>> } >>>>> return null; >>>>> } >>>>> }; >>>>> >>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>> >>>>> new Task() { >>>>> public Object call() throws Exception { >>>>> for (int i=0; i<100; i++) { >>>>> if (isCancelled()) break; >>>>> try { >>>>> doSomething(); >>>>> updateProgress(i, 100); >>>>> } catch (Exception e) { } >>>>> } >>>>> return null; >>>>> } >>>>> }; >>>>> >>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>> >>>>> new Task() { >>>>> public Object call() throws Exception { >>>>> for (int i=0; i<100; i++) { >>>>> if (isCancelled()) { >>>>> Platform.runLater(new Runnable() { >>>>> public void run() { >>>>> updateMessage("Cancelled") >>>>> } >>>>> }); >>>>> break; >>>>> } >>>>> try { >>>>> doSomething(); >>>>> updateProgress(i, 100); >>>>> } catch (Exception e) { } >>>>> } >>>>> return null; >>>>> } >>>>> }; >>>>> >>>>> new Task() { >>>>> public Object call() throws Exception { >>>>> for (int i=0; i<100; i++) { >>>>> if (isCancelled()) break; >>>>> try { >>>>> doSomething(); >>>>> updateProgress(i, 100); >>>>> } catch (Exception e) { } >>>>> } >>>>> return null; >>>>> } >>>>> >>>>> @Override protected void cancelled() { >>>>> updateMessage("Cancelled"); >>>>> } >>>>> }; >>>>> >>>>> I think this works fairly well. Anything I'm missing? >>>>> >>>>> Thanks! >>>>> Richard From steve.x.northover at oracle.com Wed Jan 4 11:05:56 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Wed, 04 Jan 2012 14:05:56 -0500 Subject: Review of solution for Cancelled Tasks In-Reply-To: <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> Message-ID: <4F04A314.1010406@oracle.com> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! Steve On 04/01/2012 2:00 PM, Richard Bair wrote: > Today, they just work (they update the progress, title, message). > > Richard > > On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: > >> What happens now when updateProgress() and friends are called for a canceled task? >> >> Steve >> >> On 04/01/2012 1:55 PM, Richard Bair wrote: >>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>> >>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>> >>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>> >>> *

Examples

>>> *

>>> * The following set of examples demonstrate some of the most common uses of >>> * Tasks. >>> *

>>> * >>> *

A Simple Loop

>>> * >>> *

>>> * The first example is a simple loop that does nothing particularly useful, >>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>> * example will simply loop and print to standard out on each loop iteration. >>> * When it completes, it returns the number of times it iterated. >>> *

>>> * >>> *

>>>   *     Task<Integer> task = new Task<Integer>() {
>>>   *         @@Override protected Integer call() throws Exception {
>>>   *             int iterations = 0;
>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>   *                 if (isCancelled()) {
>>>   *                     break;
>>>   *                 }
>>>   *                 System.out.println("Iteration " + iterations);
>>>   *             }
>>>   *             return iterations;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

>>> * First, we define what type of value is returned from this Task. In this >>> * case, we want to return the number of times we iterated, so we will >>> * specify the Task to be of type Integer by using generics. Then, within >>> * the implementation of thecall method, we iterate from >>> * 0 to 1000. On each iteration, we check to see whether this Task has >>> * been cancelled. If it has been, then we break out of the loop and return >>> * the number of times we iterated. Otherwise a message is printed to >>> * the console and the iteration count increased and we continue looping. >>> *

>>> * >>> *

>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>> * developer may cancel the task, but the task will continue running >>> * and updating both the progress and returning the incorrect result >>> * from the end of thecall method. A correct implementation >>> * of a Task will always check for cancellation. >>> *

>>> * >>> *

A Simple Loop With Progress Notification

>>> * >>> *

>>> * Similar to the previous example, except this time we will modify the >>> * progress of the Task in each iteration. Note that we have a choice >>> * to make in the case of cancellation. Do we want to set the progress back >>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>> * the progress where it was at? In this case, lets leave the progress alone >>> * and only update the message on cancellation, though updating the >>> * progress after cancellation is a perfectly valid choice. >>> *

>>> * >>> *

>>>   *     Task<Integer> task = new Task<Integer>() {
>>>   *         @@Override protected Integer call() throws Exception {
>>>   *             int iterations = 0;
>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>   *                 if (isCancelled()) {
>>>   *                     updateMessage("Cancelled");
>>>   *                     break;
>>>   *                 }
>>>   *                 updateMessage("Iteration " + iterations);
>>>   *                 (iterations, 1000);
>>>   *             }
>>>   *             return iterations;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

>>> * As before, within the for loop we check whether the Task has been >>> * cancelled. If it has been cancelled, we will update the Task's >>> * message to indicate that it has been cancelled, and then break as >>> * before. If the Task has not been cancelled, then we will update its >>> * message to indicate the current iteration and then update the >>> * progress to indicate the current progress. >>> *

>>> * >>> *

A Simple Loop With Progress Notification And Blocking Calls

>>> * >>> *

>>> * This example adds to the previous examples a blocking call. Because a >>> * blocking call may thrown an InterruptedException, and because an >>> * InterruptedException may occur as a result of the Task being cancelled, >>> * we need to be sure to handle the InterruptedException and check on the >>> * cancel state. >>> *

>>> * >>> *

>>>   *     Task<Integer> task = new Task<Integer>() {
>>>   *         @@Override protected Integer call() throws Exception {
>>>   *             int iterations = 0;
>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>   *                 if (isCancelled()) {
>>>   *                     updateMessage("Cancelled");
>>>   *                     break;
>>>   *                 }
>>>   *                 updateMessage("Iteration " + iterations);
>>>   *                 updateProgress(iterations, 1000);
>>>   *
>>>   *                 // Now block the thread for a short time, but be sure
>>>   *                 // to check the interrupted exception for cancellation!
>>>   *                 try {
>>>   *                     Thread.sleep(100);
>>>   *                 } catch (InterruptedException interrupted) {
>>>   *                     if (isCancelled()) {
>>>   *                         updateMessage("Cancelled");
>>>   *                         break;
>>>   *                     }
>>>   *                 }
>>>   *             }
>>>   *             return iterations;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

>>> * Here we have added to the body of the loop aThread.sleep >>> * call. Since this is a blocking call, I have to handle the potential >>> * InterruptedException. Within the catch block, I will check whether >>> * the Task has been cancelled, and if so, update the message accordingly >>> * and break out of the loop. >>> *

>>> * >>> *

A Task Which Takes Parameters

>>> * >>> *

>>> * Most Tasks require some parameters in order to do useful work. For >>> * example, a DeleteRecordTask needs the object or primary key to delete >>> * from the database. A ReadFileTask needs the URI of the file to be read. >>> * Because Tasks operate on a background thread, care must be taken to >>> * make sure the body of thecall method does not read or >>> * modify any shared state. There are two techniques most useful for >>> * doing this: using final variables, and passing variables to a Task >>> * during construction. >>> *

>>> * >>> *

>>> * When using a Task as an anonymous class, the most natural way to pass >>> * parameters to the Task is by using final variables. In this example, >>> * we pass to the Task the total number of times the Task should iterate. >>> *

>>> * >>> *

>>>   *     final int totalIterations = 900;
>>>   *     Task<Integer> task = new Task<Integer>() {
>>>   *         @@Override protected Integer call() throws Exception {
>>>   *             int iterations = 0;
>>>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>   *                 if (isCancelled()) {
>>>   *                     updateMessage("Cancelled");
>>>   *                     break;
>>>   *                 }
>>>   *                 updateMessage("Iteration " + iterations);
>>>   *                 updateProgress(iterations, totalIterations);
>>>   *             }
>>>   *             return iterations;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

>>> * SincetotalIterations is final, thecall >>> * method can safely read it and refer to it from a background thread. >>> *

>>> * >>> *

>>> * When writing Task libraries (as opposed to specific-use implementations), >>> * we need to use a different technique. In this case, I will create an >>> * IteratingTask which performs the same work as above. This time, since >>> * the IteratingTask is defined in its own file, it will need to have >>> * parameters passed to it in its constructor. These parameters are >>> * assigned to final variables. >>> *

>>> * >>> *

>>>   *     public class IteratingTask extends Task<Integer> {
>>>   *         private final int totalIterations;
>>>   *
>>>   *         public IteratingTask(int totalIterations) {
>>>   *             this.totalIterations = totalIterations;
>>>   *         }
>>>   *
>>>   *         @@Override protected Integer call() throws Exception {
>>>   *             int iterations = 0;
>>>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>   *                 if (isCancelled()) {
>>>   *                     updateMessage("Cancelled");
>>>   *                     break;
>>>   *                 }
>>>   *                 updateMessage("Iteration " + iterations);
>>>   *                 updateProgress(iterations, totalIterations);
>>>   *             }
>>>   *             return iterations;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

And then when used:

>>> * >>> *

>>>   *     IteratingTask task = new IteratingTask(800);
>>>   *
>>> * >>> *

In this way, parameters are passed to the IteratingTask in a safe >>> * manner, and again, are final. Thus, thecall method can >>> * safely read this state from a background thread.

>>> * >>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>> * from a background thread. Doing so may introduce race conditions. In >>> * particular, suppose you had a SaveCustomerTask which took a Customer >>> * in its constructor. Although the SaveCustomerTask may have a final >>> * reference to the Customer, if the Customer object is mutable, then it >>> * is possible that both the SaveCustomerTask and some other application code >>> * will be reading or modifying the state of the Customer from different >>> * threads. Be very careful in such cases, that while a mutable object such >>> * as this Customer is being used from a background thread, that it is >>> * not being used also from another thread. In particular, if the background >>> * thread is reading data from the database and updating the Customer object, >>> * and the Customer object is bound to scene graph nodes (such as UI >>> * controls), then there could be a violation of threading rules! For such >>> * cases, modify the Customer object from the FX Application Thread rather >>> * than from the background thread.

>>> * >>> *

>>>   *     public class UpdateCustomerTask extends Task<Customer> {
>>>   *         private final Customer customer;
>>>   *
>>>   *         public UpdateCustomerTask(Customer customer) {
>>>   *             this.customer = customer;
>>>   *         }
>>>   *
>>>   *         @@Override protected Customer call() throws Exception {
>>>   *             // pseudo-code:
>>>   *             //   query the database
>>>   *             //   read the values
>>>   *
>>>   *             // Now update the customer
>>>   *             Platform.runLater(new Runnable() {
>>>   *                 @@Override public void run() {
>>>   *                     customer.firstName(rs.getString("FirstName"));
>>>   *                     // etc
>>>   *                 }
>>>   *             });
>>>   *
>>>   *             return customer;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

A Task Which Returns No Value

>>> * >>> *

>>> * Many, if not most, Tasks should return a value upon completion. For >>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>> * created object or primary key, a "Read" Task would return the read >>> * object, an "Update" task would return the number of records updated, >>> * and a "Delete" task would return the number of records deleted. >>> *

>>> * >>> *

>>> * However sometimes there just isn't anything truly useful to return. >>> * For example, I might have a Task which writes to a file. Task has built >>> * into it a mechanism for indicating whether it has succeeded or failed >>> * along with the number of bytes written (the progress), and thus there is >>> * nothing really for me to return. In such a case, you can use the Void >>> * type. This is a special type in the Java language which can only be >>> * assigned the value ofnull. You would use it as follows: >>> *

>>> * >>> *

>>>   *     final String filePath = "/foo.txt";
>>>   *     final String contents = "Some contents";
>>>   *     Task<Void> task = new Task<Void>() {
>>>   *         @@Override protected Void call() throws Exception {
>>>   *             File file = new File(filePath);
>>>   *             FileOutputStream out = new FileOutputStream(file);
>>>   *             // ... and other code to write the contents ...
>>>   *
>>>   *             // Return null at the end of a Task of type Void
>>>   *             return null;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

A Task Which Returns An ObservableList

>>> * >>> *

Because the ListView, TableView, and other UI controls and scene graph >>> * nodes make use of ObservableList, it is common to want to create and return >>> * an ObservableList from a Task. When you do not care to display intermediate >>> * values, the easiest way to correctly write such a Task is simply to >>> * construct an ObservableList within thecall method, and then >>> * return it at the conclusion of the Task.

>>> * >>> *

>>>   *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>   *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>   *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>   *             for (int i=0; i<100; i++) {
>>>   *                 if (isCancelled()) break;
>>>   *                 Rectangle r = new Rectangle(10, 10);
>>>   *                 r.setX(10 * i);
>>>   *                 results.add(r);
>>>   *             }
>>>   *             return results;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

In the above example, we are going to create 100 rectangles and return >>> * them from this task. An ObservableList is created within the >>> *call method, populated, and then returned.

>>> * >>> *

A Task Which Returns Partial Results

>>> * >>> *

Sometimes you want to create a Task which will return partial results. >>> * Perhaps you are building a complex scene graph and want to show the >>> * scene graph as it is being constructed. Or perhaps you are reading a large >>> * amount of data over the network and want to display the entries in a >>> * TableView as the data is arriving. In such cases, there is some shared state >>> * available both to the FX Application Thread and the background thread. >>> * Great care must be taken tonever update shared state from any >>> * thread other than the FX Application Thread.

>>> * >>> *

The easiest way to do this is to expose a new property on the Task >>> * which will represent the partial result. Then make sure to use >>> *Platform.runLater when adding new items to the partial >>> * result.

>>> * >>> *

>>>   *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>   *         // Uses Java 7 diamond operator
>>>   *         private ReadOnlyObjectWrapper>   partialResults =
>>>   *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>   *
>>>   *         public final ObservableList   getPartialResults() { return partialResults; }
>>>   *         public final ReadOnlyObjectProperty>   partialResultsProperty() {
>>>   *             return partialResults.getReadOnlyProperty();
>>>   *         }
>>>   *
>>>   *         @@Override protected ObservableList   call() throws Exception {
>>>   *             for (int i=0; i<100; i++) {
>>>   *                 if (isCancelled()) break;
>>>   *                 final Rectangle r = new Rectangle(10, 10);
>>>   *                 r.setX(10 * i);
>>>   *                 Platform.runLater(new Runnable() {
>>>   *                     @@Override public void run() {
>>>   *                         partialResults.add(r);
>>>   *                     }
>>>   *                 });
>>>   *             }
>>>   *             return partialResults;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> * >>> * >>> *

A Task Which Modifies The Scene Graph

>>> * >>> *

Generally, Tasks should not interact directly with the UI. Doing so >>> * creates a tight coupling between a specific Task implementation and a >>> * specific part of your UI. However, when you do want to create such a >>> * coupling, you must ensure that you usePlatform.runLater >>> * so that any modifications of the scene graph occur on the >>> * FX Application Thread.

>>> * >>> *

>>>   *     final Group group = new Group();
>>>   *     Task<Void> task = new Task<Void>() {
>>>   *         @@Override protected Void call() throws Exception {
>>>   *             for (int i=0; i<100; i++) {
>>>   *                 if (isCancelled()) break;
>>>   *                 final Rectangle r = new Rectangle(10, 10);
>>>   *                 r.setX(10 * i);
>>>   *                 Platform.runLater(new Runnable() {
>>>   *                     @@Override public void run() {
>>>   *                         group.getChildren().add(r);
>>>   *                     }
>>>   *                 });
>>>   *             }
>>>   *             return null;
>>>   *         }
>>>   *     }
>>>   *
>>> * >>> *

Reacting To State Changes Generically

>>> * >>> *

Sometimes you may want to write a Task which updates its progress, >>> * message, text, or in some other way reacts whenever a state change >>> * happens on the Task. For example, you may want to change the status >>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>> *

>>> *

>>>   *     Task<Integer> task = new Task<Integer>() {
>>>   *         @@Override protected Integer call() throws Exception {
>>>   *             int iterations = 0;
>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>   *                 if (isCancelled()) {
>>>   *                     break;
>>>   *                 }
>>>   *                 System.out.println("Iteration " + iterations);
>>>   *             }
>>>   *             return iterations;
>>>   *         }
>>>   *
>>>   *         @@Override protected void succeeded() {
>>>   *             super.succeeded();
>>>   *             updateMessage("Done!");
>>>   *         }
>>>   *
>>>   *         @@Override protected void cancelled() {
>>>   *             super.cancelled();
>>>   *             updateMessage("Cancelled!");
>>>   *         }
>>>   *
>>>   *         @@Override protected void failed() {
>>>   *             super.failed();
>>>   *             updateMessage("Failed!");
>>>   *         }
>>>   *     }
>>>   *
>>> >>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>> >>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>> >>> Thanks everybody for your very valuable input! >>> Richard >>> >>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>> >>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>> >>>> Steve >>>> >>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>> Hi Richard, >>>>> >>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>> faulty code. >>>>> >>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>> especially the FutureTask class. I.e. provide an API to check >>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>> interrupt a thread: >>>>> >>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>> >>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>> of shitty code being written (surprise!), especially when threads are >>>>> involved. >>>>> >>>>> Cheers, Roman >>>>> >>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>> >>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>> >>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>> >>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>> >>>>>> If you write a Task, and do nothing smart: >>>>>> >>>>>> new Task() { >>>>>> public Object call() throws Exception { >>>>>> for (int i=0; i<100; i++) { >>>>>> doSomething(); >>>>>> } >>>>>> return null; >>>>>> } >>>>>> }; >>>>>> >>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>> >>>>>> new Task() { >>>>>> public Object call() throws Exception { >>>>>> for (int i=0; i<100; i++) { >>>>>> doSomething(); >>>>>> updateProgress(i, 100); >>>>>> } >>>>>> return null; >>>>>> } >>>>>> }; >>>>>> >>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>> >>>>>> In this case...: >>>>>> >>>>>> new Task() { >>>>>> public Object call() throws Exception { >>>>>> for (int i=0; i<100; i++) { >>>>>> try { >>>>>> doSomething(); >>>>>> updateProgress(i, 100); >>>>>> } catch (Exception e) { } >>>>>> } >>>>>> return null; >>>>>> } >>>>>> }; >>>>>> >>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>> >>>>>> new Task() { >>>>>> public Object call() throws Exception { >>>>>> for (int i=0; i<100; i++) { >>>>>> if (isCancelled()) break; >>>>>> try { >>>>>> doSomething(); >>>>>> updateProgress(i, 100); >>>>>> } catch (Exception e) { } >>>>>> } >>>>>> return null; >>>>>> } >>>>>> }; >>>>>> >>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>> >>>>>> new Task() { >>>>>> public Object call() throws Exception { >>>>>> for (int i=0; i<100; i++) { >>>>>> if (isCancelled()) { >>>>>> Platform.runLater(new Runnable() { >>>>>> public void run() { >>>>>> updateMessage("Cancelled") >>>>>> } >>>>>> }); >>>>>> break; >>>>>> } >>>>>> try { >>>>>> doSomething(); >>>>>> updateProgress(i, 100); >>>>>> } catch (Exception e) { } >>>>>> } >>>>>> return null; >>>>>> } >>>>>> }; >>>>>> >>>>>> new Task() { >>>>>> public Object call() throws Exception { >>>>>> for (int i=0; i<100; i++) { >>>>>> if (isCancelled()) break; >>>>>> try { >>>>>> doSomething(); >>>>>> updateProgress(i, 100); >>>>>> } catch (Exception e) { } >>>>>> } >>>>>> return null; >>>>>> } >>>>>> >>>>>> @Override protected void cancelled() { >>>>>> updateMessage("Cancelled"); >>>>>> } >>>>>> }; >>>>>> >>>>>> I think this works fairly well. Anything I'm missing? >>>>>> >>>>>> Thanks! >>>>>> Richard From Richard.Bair at oracle.com Wed Jan 4 11:11:12 2012 From: Richard.Bair at oracle.com (Richard Bair) Date: Wed, 4 Jan 2012 11:11:12 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F04A314.1010406@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> Message-ID: Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. Richard On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: > Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! > > Steve > > On 04/01/2012 2:00 PM, Richard Bair wrote: >> Today, they just work (they update the progress, title, message). >> >> Richard >> >> On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: >> >>> What happens now when updateProgress() and friends are called for a canceled task? >>> >>> Steve >>> >>> On 04/01/2012 1:55 PM, Richard Bair wrote: >>>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>>> >>>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>>> >>>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>>> >>>> *

Examples

>>>> *

>>>> * The following set of examples demonstrate some of the most common uses of >>>> * Tasks. >>>> *

>>>> * >>>> *

A Simple Loop

>>>> * >>>> *

>>>> * The first example is a simple loop that does nothing particularly useful, >>>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>>> * example will simply loop and print to standard out on each loop iteration. >>>> * When it completes, it returns the number of times it iterated. >>>> *

>>>> * >>>> *

>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>  *         @@Override protected Integer call() throws Exception {
>>>>  *             int iterations = 0;
>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>  *                 if (isCancelled()) {
>>>>  *                     break;
>>>>  *                 }
>>>>  *                 System.out.println("Iteration " + iterations);
>>>>  *             }
>>>>  *             return iterations;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

>>>> * First, we define what type of value is returned from this Task. In this >>>> * case, we want to return the number of times we iterated, so we will >>>> * specify the Task to be of type Integer by using generics. Then, within >>>> * the implementation of thecall method, we iterate from >>>> * 0 to 1000. On each iteration, we check to see whether this Task has >>>> * been cancelled. If it has been, then we break out of the loop and return >>>> * the number of times we iterated. Otherwise a message is printed to >>>> * the console and the iteration count increased and we continue looping. >>>> *

>>>> * >>>> *

>>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>>> * developer may cancel the task, but the task will continue running >>>> * and updating both the progress and returning the incorrect result >>>> * from the end of thecall method. A correct implementation >>>> * of a Task will always check for cancellation. >>>> *

>>>> * >>>> *

A Simple Loop With Progress Notification

>>>> * >>>> *

>>>> * Similar to the previous example, except this time we will modify the >>>> * progress of the Task in each iteration. Note that we have a choice >>>> * to make in the case of cancellation. Do we want to set the progress back >>>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>>> * the progress where it was at? In this case, lets leave the progress alone >>>> * and only update the message on cancellation, though updating the >>>> * progress after cancellation is a perfectly valid choice. >>>> *

>>>> * >>>> *

>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>  *         @@Override protected Integer call() throws Exception {
>>>>  *             int iterations = 0;
>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>  *                 if (isCancelled()) {
>>>>  *                     updateMessage("Cancelled");
>>>>  *                     break;
>>>>  *                 }
>>>>  *                 updateMessage("Iteration " + iterations);
>>>>  *                 (iterations, 1000);
>>>>  *             }
>>>>  *             return iterations;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

>>>> * As before, within the for loop we check whether the Task has been >>>> * cancelled. If it has been cancelled, we will update the Task's >>>> * message to indicate that it has been cancelled, and then break as >>>> * before. If the Task has not been cancelled, then we will update its >>>> * message to indicate the current iteration and then update the >>>> * progress to indicate the current progress. >>>> *

>>>> * >>>> *

A Simple Loop With Progress Notification And Blocking Calls

>>>> * >>>> *

>>>> * This example adds to the previous examples a blocking call. Because a >>>> * blocking call may thrown an InterruptedException, and because an >>>> * InterruptedException may occur as a result of the Task being cancelled, >>>> * we need to be sure to handle the InterruptedException and check on the >>>> * cancel state. >>>> *

>>>> * >>>> *

>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>  *         @@Override protected Integer call() throws Exception {
>>>>  *             int iterations = 0;
>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>  *                 if (isCancelled()) {
>>>>  *                     updateMessage("Cancelled");
>>>>  *                     break;
>>>>  *                 }
>>>>  *                 updateMessage("Iteration " + iterations);
>>>>  *                 updateProgress(iterations, 1000);
>>>>  *
>>>>  *                 // Now block the thread for a short time, but be sure
>>>>  *                 // to check the interrupted exception for cancellation!
>>>>  *                 try {
>>>>  *                     Thread.sleep(100);
>>>>  *                 } catch (InterruptedException interrupted) {
>>>>  *                     if (isCancelled()) {
>>>>  *                         updateMessage("Cancelled");
>>>>  *                         break;
>>>>  *                     }
>>>>  *                 }
>>>>  *             }
>>>>  *             return iterations;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

>>>> * Here we have added to the body of the loop aThread.sleep >>>> * call. Since this is a blocking call, I have to handle the potential >>>> * InterruptedException. Within the catch block, I will check whether >>>> * the Task has been cancelled, and if so, update the message accordingly >>>> * and break out of the loop. >>>> *

>>>> * >>>> *

A Task Which Takes Parameters

>>>> * >>>> *

>>>> * Most Tasks require some parameters in order to do useful work. For >>>> * example, a DeleteRecordTask needs the object or primary key to delete >>>> * from the database. A ReadFileTask needs the URI of the file to be read. >>>> * Because Tasks operate on a background thread, care must be taken to >>>> * make sure the body of thecall method does not read or >>>> * modify any shared state. There are two techniques most useful for >>>> * doing this: using final variables, and passing variables to a Task >>>> * during construction. >>>> *

>>>> * >>>> *

>>>> * When using a Task as an anonymous class, the most natural way to pass >>>> * parameters to the Task is by using final variables. In this example, >>>> * we pass to the Task the total number of times the Task should iterate. >>>> *

>>>> * >>>> *

>>>>  *     final int totalIterations = 900;
>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>  *         @@Override protected Integer call() throws Exception {
>>>>  *             int iterations = 0;
>>>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>  *                 if (isCancelled()) {
>>>>  *                     updateMessage("Cancelled");
>>>>  *                     break;
>>>>  *                 }
>>>>  *                 updateMessage("Iteration " + iterations);
>>>>  *                 updateProgress(iterations, totalIterations);
>>>>  *             }
>>>>  *             return iterations;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

>>>> * SincetotalIterations is final, thecall >>>> * method can safely read it and refer to it from a background thread. >>>> *

>>>> * >>>> *

>>>> * When writing Task libraries (as opposed to specific-use implementations), >>>> * we need to use a different technique. In this case, I will create an >>>> * IteratingTask which performs the same work as above. This time, since >>>> * the IteratingTask is defined in its own file, it will need to have >>>> * parameters passed to it in its constructor. These parameters are >>>> * assigned to final variables. >>>> *

>>>> * >>>> *

>>>>  *     public class IteratingTask extends Task<Integer> {
>>>>  *         private final int totalIterations;
>>>>  *
>>>>  *         public IteratingTask(int totalIterations) {
>>>>  *             this.totalIterations = totalIterations;
>>>>  *         }
>>>>  *
>>>>  *         @@Override protected Integer call() throws Exception {
>>>>  *             int iterations = 0;
>>>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>  *                 if (isCancelled()) {
>>>>  *                     updateMessage("Cancelled");
>>>>  *                     break;
>>>>  *                 }
>>>>  *                 updateMessage("Iteration " + iterations);
>>>>  *                 updateProgress(iterations, totalIterations);
>>>>  *             }
>>>>  *             return iterations;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

And then when used:

>>>> * >>>> *

>>>>  *     IteratingTask task = new IteratingTask(800);
>>>>  *
>>>> * >>>> *

In this way, parameters are passed to the IteratingTask in a safe >>>> * manner, and again, are final. Thus, thecall method can >>>> * safely read this state from a background thread.

>>>> * >>>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>>> * from a background thread. Doing so may introduce race conditions. In >>>> * particular, suppose you had a SaveCustomerTask which took a Customer >>>> * in its constructor. Although the SaveCustomerTask may have a final >>>> * reference to the Customer, if the Customer object is mutable, then it >>>> * is possible that both the SaveCustomerTask and some other application code >>>> * will be reading or modifying the state of the Customer from different >>>> * threads. Be very careful in such cases, that while a mutable object such >>>> * as this Customer is being used from a background thread, that it is >>>> * not being used also from another thread. In particular, if the background >>>> * thread is reading data from the database and updating the Customer object, >>>> * and the Customer object is bound to scene graph nodes (such as UI >>>> * controls), then there could be a violation of threading rules! For such >>>> * cases, modify the Customer object from the FX Application Thread rather >>>> * than from the background thread.

>>>> * >>>> *

>>>>  *     public class UpdateCustomerTask extends Task<Customer> {
>>>>  *         private final Customer customer;
>>>>  *
>>>>  *         public UpdateCustomerTask(Customer customer) {
>>>>  *             this.customer = customer;
>>>>  *         }
>>>>  *
>>>>  *         @@Override protected Customer call() throws Exception {
>>>>  *             // pseudo-code:
>>>>  *             //   query the database
>>>>  *             //   read the values
>>>>  *
>>>>  *             // Now update the customer
>>>>  *             Platform.runLater(new Runnable() {
>>>>  *                 @@Override public void run() {
>>>>  *                     customer.firstName(rs.getString("FirstName"));
>>>>  *                     // etc
>>>>  *                 }
>>>>  *             });
>>>>  *
>>>>  *             return customer;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

A Task Which Returns No Value

>>>> * >>>> *

>>>> * Many, if not most, Tasks should return a value upon completion. For >>>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>>> * created object or primary key, a "Read" Task would return the read >>>> * object, an "Update" task would return the number of records updated, >>>> * and a "Delete" task would return the number of records deleted. >>>> *

>>>> * >>>> *

>>>> * However sometimes there just isn't anything truly useful to return. >>>> * For example, I might have a Task which writes to a file. Task has built >>>> * into it a mechanism for indicating whether it has succeeded or failed >>>> * along with the number of bytes written (the progress), and thus there is >>>> * nothing really for me to return. In such a case, you can use the Void >>>> * type. This is a special type in the Java language which can only be >>>> * assigned the value ofnull. You would use it as follows: >>>> *

>>>> * >>>> *

>>>>  *     final String filePath = "/foo.txt";
>>>>  *     final String contents = "Some contents";
>>>>  *     Task<Void> task = new Task<Void>() {
>>>>  *         @@Override protected Void call() throws Exception {
>>>>  *             File file = new File(filePath);
>>>>  *             FileOutputStream out = new FileOutputStream(file);
>>>>  *             // ... and other code to write the contents ...
>>>>  *
>>>>  *             // Return null at the end of a Task of type Void
>>>>  *             return null;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

A Task Which Returns An ObservableList

>>>> * >>>> *

Because the ListView, TableView, and other UI controls and scene graph >>>> * nodes make use of ObservableList, it is common to want to create and return >>>> * an ObservableList from a Task. When you do not care to display intermediate >>>> * values, the easiest way to correctly write such a Task is simply to >>>> * construct an ObservableList within thecall method, and then >>>> * return it at the conclusion of the Task.

>>>> * >>>> *

>>>>  *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>>  *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>>  *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>>  *             for (int i=0; i<100; i++) {
>>>>  *                 if (isCancelled()) break;
>>>>  *                 Rectangle r = new Rectangle(10, 10);
>>>>  *                 r.setX(10 * i);
>>>>  *                 results.add(r);
>>>>  *             }
>>>>  *             return results;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

In the above example, we are going to create 100 rectangles and return >>>> * them from this task. An ObservableList is created within the >>>> *call method, populated, and then returned.

>>>> * >>>> *

A Task Which Returns Partial Results

>>>> * >>>> *

Sometimes you want to create a Task which will return partial results. >>>> * Perhaps you are building a complex scene graph and want to show the >>>> * scene graph as it is being constructed. Or perhaps you are reading a large >>>> * amount of data over the network and want to display the entries in a >>>> * TableView as the data is arriving. In such cases, there is some shared state >>>> * available both to the FX Application Thread and the background thread. >>>> * Great care must be taken tonever update shared state from any >>>> * thread other than the FX Application Thread.

>>>> * >>>> *

The easiest way to do this is to expose a new property on the Task >>>> * which will represent the partial result. Then make sure to use >>>> *Platform.runLater when adding new items to the partial >>>> * result.

>>>> * >>>> *

>>>>  *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>>  *         // Uses Java 7 diamond operator
>>>>  *         private ReadOnlyObjectWrapper>   partialResults =
>>>>  *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>>  *
>>>>  *         public final ObservableList   getPartialResults() { return partialResults; }
>>>>  *         public final ReadOnlyObjectProperty>   partialResultsProperty() {
>>>>  *             return partialResults.getReadOnlyProperty();
>>>>  *         }
>>>>  *
>>>>  *         @@Override protected ObservableList   call() throws Exception {
>>>>  *             for (int i=0; i<100; i++) {
>>>>  *                 if (isCancelled()) break;
>>>>  *                 final Rectangle r = new Rectangle(10, 10);
>>>>  *                 r.setX(10 * i);
>>>>  *                 Platform.runLater(new Runnable() {
>>>>  *                     @@Override public void run() {
>>>>  *                         partialResults.add(r);
>>>>  *                     }
>>>>  *                 });
>>>>  *             }
>>>>  *             return partialResults;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> * >>>> * >>>> *

A Task Which Modifies The Scene Graph

>>>> * >>>> *

Generally, Tasks should not interact directly with the UI. Doing so >>>> * creates a tight coupling between a specific Task implementation and a >>>> * specific part of your UI. However, when you do want to create such a >>>> * coupling, you must ensure that you usePlatform.runLater >>>> * so that any modifications of the scene graph occur on the >>>> * FX Application Thread.

>>>> * >>>> *

>>>>  *     final Group group = new Group();
>>>>  *     Task<Void> task = new Task<Void>() {
>>>>  *         @@Override protected Void call() throws Exception {
>>>>  *             for (int i=0; i<100; i++) {
>>>>  *                 if (isCancelled()) break;
>>>>  *                 final Rectangle r = new Rectangle(10, 10);
>>>>  *                 r.setX(10 * i);
>>>>  *                 Platform.runLater(new Runnable() {
>>>>  *                     @@Override public void run() {
>>>>  *                         group.getChildren().add(r);
>>>>  *                     }
>>>>  *                 });
>>>>  *             }
>>>>  *             return null;
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> * >>>> *

Reacting To State Changes Generically

>>>> * >>>> *

Sometimes you may want to write a Task which updates its progress, >>>> * message, text, or in some other way reacts whenever a state change >>>> * happens on the Task. For example, you may want to change the status >>>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>>> *

>>>> *

>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>  *         @@Override protected Integer call() throws Exception {
>>>>  *             int iterations = 0;
>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>  *                 if (isCancelled()) {
>>>>  *                     break;
>>>>  *                 }
>>>>  *                 System.out.println("Iteration " + iterations);
>>>>  *             }
>>>>  *             return iterations;
>>>>  *         }
>>>>  *
>>>>  *         @@Override protected void succeeded() {
>>>>  *             super.succeeded();
>>>>  *             updateMessage("Done!");
>>>>  *         }
>>>>  *
>>>>  *         @@Override protected void cancelled() {
>>>>  *             super.cancelled();
>>>>  *             updateMessage("Cancelled!");
>>>>  *         }
>>>>  *
>>>>  *         @@Override protected void failed() {
>>>>  *             super.failed();
>>>>  *             updateMessage("Failed!");
>>>>  *         }
>>>>  *     }
>>>>  *
>>>> >>>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>>> >>>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>>> >>>> Thanks everybody for your very valuable input! >>>> Richard >>>> >>>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>>> >>>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>>> >>>>> Steve >>>>> >>>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>>> Hi Richard, >>>>>> >>>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>>> faulty code. >>>>>> >>>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>>> especially the FutureTask class. I.e. provide an API to check >>>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>>> interrupt a thread: >>>>>> >>>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>>> >>>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>>> of shitty code being written (surprise!), especially when threads are >>>>>> involved. >>>>>> >>>>>> Cheers, Roman >>>>>> >>>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>>> >>>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>>> >>>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>>> >>>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>>> >>>>>>> If you write a Task, and do nothing smart: >>>>>>> >>>>>>> new Task() { >>>>>>> public Object call() throws Exception { >>>>>>> for (int i=0; i<100; i++) { >>>>>>> doSomething(); >>>>>>> } >>>>>>> return null; >>>>>>> } >>>>>>> }; >>>>>>> >>>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>>> >>>>>>> new Task() { >>>>>>> public Object call() throws Exception { >>>>>>> for (int i=0; i<100; i++) { >>>>>>> doSomething(); >>>>>>> updateProgress(i, 100); >>>>>>> } >>>>>>> return null; >>>>>>> } >>>>>>> }; >>>>>>> >>>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>>> >>>>>>> In this case...: >>>>>>> >>>>>>> new Task() { >>>>>>> public Object call() throws Exception { >>>>>>> for (int i=0; i<100; i++) { >>>>>>> try { >>>>>>> doSomething(); >>>>>>> updateProgress(i, 100); >>>>>>> } catch (Exception e) { } >>>>>>> } >>>>>>> return null; >>>>>>> } >>>>>>> }; >>>>>>> >>>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>>> >>>>>>> new Task() { >>>>>>> public Object call() throws Exception { >>>>>>> for (int i=0; i<100; i++) { >>>>>>> if (isCancelled()) break; >>>>>>> try { >>>>>>> doSomething(); >>>>>>> updateProgress(i, 100); >>>>>>> } catch (Exception e) { } >>>>>>> } >>>>>>> return null; >>>>>>> } >>>>>>> }; >>>>>>> >>>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>>> >>>>>>> new Task() { >>>>>>> public Object call() throws Exception { >>>>>>> for (int i=0; i<100; i++) { >>>>>>> if (isCancelled()) { >>>>>>> Platform.runLater(new Runnable() { >>>>>>> public void run() { >>>>>>> updateMessage("Cancelled") >>>>>>> } >>>>>>> }); >>>>>>> break; >>>>>>> } >>>>>>> try { >>>>>>> doSomething(); >>>>>>> updateProgress(i, 100); >>>>>>> } catch (Exception e) { } >>>>>>> } >>>>>>> return null; >>>>>>> } >>>>>>> }; >>>>>>> >>>>>>> new Task() { >>>>>>> public Object call() throws Exception { >>>>>>> for (int i=0; i<100; i++) { >>>>>>> if (isCancelled()) break; >>>>>>> try { >>>>>>> doSomething(); >>>>>>> updateProgress(i, 100); >>>>>>> } catch (Exception e) { } >>>>>>> } >>>>>>> return null; >>>>>>> } >>>>>>> >>>>>>> @Override protected void cancelled() { >>>>>>> updateMessage("Cancelled"); >>>>>>> } >>>>>>> }; >>>>>>> >>>>>>> I think this works fairly well. Anything I'm missing? >>>>>>> >>>>>>> Thanks! >>>>>>> Richard From steve.x.northover at oracle.com Wed Jan 4 11:24:08 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Wed, 04 Jan 2012 14:24:08 -0500 Subject: Review of solution for Cancelled Tasks In-Reply-To: References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> Message-ID: <4F04A758.8090404@oracle.com> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. Steve On 04/01/2012 2:11 PM, Richard Bair wrote: > Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. > > However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. > > So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. > > In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. > > Richard > > > On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: > >> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >> >> Steve >> >> On 04/01/2012 2:00 PM, Richard Bair wrote: >>> Today, they just work (they update the progress, title, message). >>> >>> Richard >>> >>> On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: >>> >>>> What happens now when updateProgress() and friends are called for a canceled task? >>>> >>>> Steve >>>> >>>> On 04/01/2012 1:55 PM, Richard Bair wrote: >>>>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>>>> >>>>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>>>> >>>>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>>>> >>>>> *

Examples

>>>>> *

>>>>> * The following set of examples demonstrate some of the most common uses of >>>>> * Tasks. >>>>> *

>>>>> * >>>>> *

A Simple Loop

>>>>> * >>>>> *

>>>>> * The first example is a simple loop that does nothing particularly useful, >>>>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>>>> * example will simply loop and print to standard out on each loop iteration. >>>>> * When it completes, it returns the number of times it iterated. >>>>> *

>>>>> * >>>>> *

>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>   *             int iterations = 0;
>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>   *                 if (isCancelled()) {
>>>>>   *                     break;
>>>>>   *                 }
>>>>>   *                 System.out.println("Iteration " + iterations);
>>>>>   *             }
>>>>>   *             return iterations;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

>>>>> * First, we define what type of value is returned from this Task. In this >>>>> * case, we want to return the number of times we iterated, so we will >>>>> * specify the Task to be of type Integer by using generics. Then, within >>>>> * the implementation of thecall method, we iterate from >>>>> * 0 to 1000. On each iteration, we check to see whether this Task has >>>>> * been cancelled. If it has been, then we break out of the loop and return >>>>> * the number of times we iterated. Otherwise a message is printed to >>>>> * the console and the iteration count increased and we continue looping. >>>>> *

>>>>> * >>>>> *

>>>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>>>> * developer may cancel the task, but the task will continue running >>>>> * and updating both the progress and returning the incorrect result >>>>> * from the end of thecall method. A correct implementation >>>>> * of a Task will always check for cancellation. >>>>> *

>>>>> * >>>>> *

A Simple Loop With Progress Notification

>>>>> * >>>>> *

>>>>> * Similar to the previous example, except this time we will modify the >>>>> * progress of the Task in each iteration. Note that we have a choice >>>>> * to make in the case of cancellation. Do we want to set the progress back >>>>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>>>> * the progress where it was at? In this case, lets leave the progress alone >>>>> * and only update the message on cancellation, though updating the >>>>> * progress after cancellation is a perfectly valid choice. >>>>> *

>>>>> * >>>>> *

>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>   *             int iterations = 0;
>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>   *                 if (isCancelled()) {
>>>>>   *                     updateMessage("Cancelled");
>>>>>   *                     break;
>>>>>   *                 }
>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>   *                 (iterations, 1000);
>>>>>   *             }
>>>>>   *             return iterations;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

>>>>> * As before, within the for loop we check whether the Task has been >>>>> * cancelled. If it has been cancelled, we will update the Task's >>>>> * message to indicate that it has been cancelled, and then break as >>>>> * before. If the Task has not been cancelled, then we will update its >>>>> * message to indicate the current iteration and then update the >>>>> * progress to indicate the current progress. >>>>> *

>>>>> * >>>>> *

A Simple Loop With Progress Notification And Blocking Calls

>>>>> * >>>>> *

>>>>> * This example adds to the previous examples a blocking call. Because a >>>>> * blocking call may thrown an InterruptedException, and because an >>>>> * InterruptedException may occur as a result of the Task being cancelled, >>>>> * we need to be sure to handle the InterruptedException and check on the >>>>> * cancel state. >>>>> *

>>>>> * >>>>> *

>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>   *             int iterations = 0;
>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>   *                 if (isCancelled()) {
>>>>>   *                     updateMessage("Cancelled");
>>>>>   *                     break;
>>>>>   *                 }
>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>   *                 updateProgress(iterations, 1000);
>>>>>   *
>>>>>   *                 // Now block the thread for a short time, but be sure
>>>>>   *                 // to check the interrupted exception for cancellation!
>>>>>   *                 try {
>>>>>   *                     Thread.sleep(100);
>>>>>   *                 } catch (InterruptedException interrupted) {
>>>>>   *                     if (isCancelled()) {
>>>>>   *                         updateMessage("Cancelled");
>>>>>   *                         break;
>>>>>   *                     }
>>>>>   *                 }
>>>>>   *             }
>>>>>   *             return iterations;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

>>>>> * Here we have added to the body of the loop aThread.sleep >>>>> * call. Since this is a blocking call, I have to handle the potential >>>>> * InterruptedException. Within the catch block, I will check whether >>>>> * the Task has been cancelled, and if so, update the message accordingly >>>>> * and break out of the loop. >>>>> *

>>>>> * >>>>> *

A Task Which Takes Parameters

>>>>> * >>>>> *

>>>>> * Most Tasks require some parameters in order to do useful work. For >>>>> * example, a DeleteRecordTask needs the object or primary key to delete >>>>> * from the database. A ReadFileTask needs the URI of the file to be read. >>>>> * Because Tasks operate on a background thread, care must be taken to >>>>> * make sure the body of thecall method does not read or >>>>> * modify any shared state. There are two techniques most useful for >>>>> * doing this: using final variables, and passing variables to a Task >>>>> * during construction. >>>>> *

>>>>> * >>>>> *

>>>>> * When using a Task as an anonymous class, the most natural way to pass >>>>> * parameters to the Task is by using final variables. In this example, >>>>> * we pass to the Task the total number of times the Task should iterate. >>>>> *

>>>>> * >>>>> *

>>>>>   *     final int totalIterations = 900;
>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>   *             int iterations = 0;
>>>>>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>   *                 if (isCancelled()) {
>>>>>   *                     updateMessage("Cancelled");
>>>>>   *                     break;
>>>>>   *                 }
>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>   *                 updateProgress(iterations, totalIterations);
>>>>>   *             }
>>>>>   *             return iterations;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

>>>>> * SincetotalIterations is final, thecall >>>>> * method can safely read it and refer to it from a background thread. >>>>> *

>>>>> * >>>>> *

>>>>> * When writing Task libraries (as opposed to specific-use implementations), >>>>> * we need to use a different technique. In this case, I will create an >>>>> * IteratingTask which performs the same work as above. This time, since >>>>> * the IteratingTask is defined in its own file, it will need to have >>>>> * parameters passed to it in its constructor. These parameters are >>>>> * assigned to final variables. >>>>> *

>>>>> * >>>>> *

>>>>>   *     public class IteratingTask extends Task<Integer> {
>>>>>   *         private final int totalIterations;
>>>>>   *
>>>>>   *         public IteratingTask(int totalIterations) {
>>>>>   *             this.totalIterations = totalIterations;
>>>>>   *         }
>>>>>   *
>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>   *             int iterations = 0;
>>>>>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>   *                 if (isCancelled()) {
>>>>>   *                     updateMessage("Cancelled");
>>>>>   *                     break;
>>>>>   *                 }
>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>   *                 updateProgress(iterations, totalIterations);
>>>>>   *             }
>>>>>   *             return iterations;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

And then when used:

>>>>> * >>>>> *

>>>>>   *     IteratingTask task = new IteratingTask(800);
>>>>>   *
>>>>> * >>>>> *

In this way, parameters are passed to the IteratingTask in a safe >>>>> * manner, and again, are final. Thus, thecall method can >>>>> * safely read this state from a background thread.

>>>>> * >>>>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>>>> * from a background thread. Doing so may introduce race conditions. In >>>>> * particular, suppose you had a SaveCustomerTask which took a Customer >>>>> * in its constructor. Although the SaveCustomerTask may have a final >>>>> * reference to the Customer, if the Customer object is mutable, then it >>>>> * is possible that both the SaveCustomerTask and some other application code >>>>> * will be reading or modifying the state of the Customer from different >>>>> * threads. Be very careful in such cases, that while a mutable object such >>>>> * as this Customer is being used from a background thread, that it is >>>>> * not being used also from another thread. In particular, if the background >>>>> * thread is reading data from the database and updating the Customer object, >>>>> * and the Customer object is bound to scene graph nodes (such as UI >>>>> * controls), then there could be a violation of threading rules! For such >>>>> * cases, modify the Customer object from the FX Application Thread rather >>>>> * than from the background thread.

>>>>> * >>>>> *

>>>>>   *     public class UpdateCustomerTask extends Task<Customer> {
>>>>>   *         private final Customer customer;
>>>>>   *
>>>>>   *         public UpdateCustomerTask(Customer customer) {
>>>>>   *             this.customer = customer;
>>>>>   *         }
>>>>>   *
>>>>>   *         @@Override protected Customer call() throws Exception {
>>>>>   *             // pseudo-code:
>>>>>   *             //   query the database
>>>>>   *             //   read the values
>>>>>   *
>>>>>   *             // Now update the customer
>>>>>   *             Platform.runLater(new Runnable() {
>>>>>   *                 @@Override public void run() {
>>>>>   *                     customer.firstName(rs.getString("FirstName"));
>>>>>   *                     // etc
>>>>>   *                 }
>>>>>   *             });
>>>>>   *
>>>>>   *             return customer;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

A Task Which Returns No Value

>>>>> * >>>>> *

>>>>> * Many, if not most, Tasks should return a value upon completion. For >>>>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>>>> * created object or primary key, a "Read" Task would return the read >>>>> * object, an "Update" task would return the number of records updated, >>>>> * and a "Delete" task would return the number of records deleted. >>>>> *

>>>>> * >>>>> *

>>>>> * However sometimes there just isn't anything truly useful to return. >>>>> * For example, I might have a Task which writes to a file. Task has built >>>>> * into it a mechanism for indicating whether it has succeeded or failed >>>>> * along with the number of bytes written (the progress), and thus there is >>>>> * nothing really for me to return. In such a case, you can use the Void >>>>> * type. This is a special type in the Java language which can only be >>>>> * assigned the value ofnull. You would use it as follows: >>>>> *

>>>>> * >>>>> *

>>>>>   *     final String filePath = "/foo.txt";
>>>>>   *     final String contents = "Some contents";
>>>>>   *     Task<Void> task = new Task<Void>() {
>>>>>   *         @@Override protected Void call() throws Exception {
>>>>>   *             File file = new File(filePath);
>>>>>   *             FileOutputStream out = new FileOutputStream(file);
>>>>>   *             // ... and other code to write the contents ...
>>>>>   *
>>>>>   *             // Return null at the end of a Task of type Void
>>>>>   *             return null;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

A Task Which Returns An ObservableList

>>>>> * >>>>> *

Because the ListView, TableView, and other UI controls and scene graph >>>>> * nodes make use of ObservableList, it is common to want to create and return >>>>> * an ObservableList from a Task. When you do not care to display intermediate >>>>> * values, the easiest way to correctly write such a Task is simply to >>>>> * construct an ObservableList within thecall method, and then >>>>> * return it at the conclusion of the Task.

>>>>> * >>>>> *

>>>>>   *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>>>   *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>>>   *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>>>   *             for (int i=0; i<100; i++) {
>>>>>   *                 if (isCancelled()) break;
>>>>>   *                 Rectangle r = new Rectangle(10, 10);
>>>>>   *                 r.setX(10 * i);
>>>>>   *                 results.add(r);
>>>>>   *             }
>>>>>   *             return results;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

In the above example, we are going to create 100 rectangles and return >>>>> * them from this task. An ObservableList is created within the >>>>> *call method, populated, and then returned.

>>>>> * >>>>> *

A Task Which Returns Partial Results

>>>>> * >>>>> *

Sometimes you want to create a Task which will return partial results. >>>>> * Perhaps you are building a complex scene graph and want to show the >>>>> * scene graph as it is being constructed. Or perhaps you are reading a large >>>>> * amount of data over the network and want to display the entries in a >>>>> * TableView as the data is arriving. In such cases, there is some shared state >>>>> * available both to the FX Application Thread and the background thread. >>>>> * Great care must be taken tonever update shared state from any >>>>> * thread other than the FX Application Thread.

>>>>> * >>>>> *

The easiest way to do this is to expose a new property on the Task >>>>> * which will represent the partial result. Then make sure to use >>>>> *Platform.runLater when adding new items to the partial >>>>> * result.

>>>>> * >>>>> *

>>>>>   *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>>>   *         // Uses Java 7 diamond operator
>>>>>   *         private ReadOnlyObjectWrapper>    partialResults =
>>>>>   *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>>>   *
>>>>>   *         public final ObservableList    getPartialResults() { return partialResults; }
>>>>>   *         public final ReadOnlyObjectProperty>    partialResultsProperty() {
>>>>>   *             return partialResults.getReadOnlyProperty();
>>>>>   *         }
>>>>>   *
>>>>>   *         @@Override protected ObservableList    call() throws Exception {
>>>>>   *             for (int i=0; i<100; i++) {
>>>>>   *                 if (isCancelled()) break;
>>>>>   *                 final Rectangle r = new Rectangle(10, 10);
>>>>>   *                 r.setX(10 * i);
>>>>>   *                 Platform.runLater(new Runnable() {
>>>>>   *                     @@Override public void run() {
>>>>>   *                         partialResults.add(r);
>>>>>   *                     }
>>>>>   *                 });
>>>>>   *             }
>>>>>   *             return partialResults;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> * >>>>> * >>>>> *

A Task Which Modifies The Scene Graph

>>>>> * >>>>> *

Generally, Tasks should not interact directly with the UI. Doing so >>>>> * creates a tight coupling between a specific Task implementation and a >>>>> * specific part of your UI. However, when you do want to create such a >>>>> * coupling, you must ensure that you usePlatform.runLater >>>>> * so that any modifications of the scene graph occur on the >>>>> * FX Application Thread.

>>>>> * >>>>> *

>>>>>   *     final Group group = new Group();
>>>>>   *     Task<Void> task = new Task<Void>() {
>>>>>   *         @@Override protected Void call() throws Exception {
>>>>>   *             for (int i=0; i<100; i++) {
>>>>>   *                 if (isCancelled()) break;
>>>>>   *                 final Rectangle r = new Rectangle(10, 10);
>>>>>   *                 r.setX(10 * i);
>>>>>   *                 Platform.runLater(new Runnable() {
>>>>>   *                     @@Override public void run() {
>>>>>   *                         group.getChildren().add(r);
>>>>>   *                     }
>>>>>   *                 });
>>>>>   *             }
>>>>>   *             return null;
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> * >>>>> *

Reacting To State Changes Generically

>>>>> * >>>>> *

Sometimes you may want to write a Task which updates its progress, >>>>> * message, text, or in some other way reacts whenever a state change >>>>> * happens on the Task. For example, you may want to change the status >>>>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>>>> *

>>>>> *

>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>   *             int iterations = 0;
>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>   *                 if (isCancelled()) {
>>>>>   *                     break;
>>>>>   *                 }
>>>>>   *                 System.out.println("Iteration " + iterations);
>>>>>   *             }
>>>>>   *             return iterations;
>>>>>   *         }
>>>>>   *
>>>>>   *         @@Override protected void succeeded() {
>>>>>   *             super.succeeded();
>>>>>   *             updateMessage("Done!");
>>>>>   *         }
>>>>>   *
>>>>>   *         @@Override protected void cancelled() {
>>>>>   *             super.cancelled();
>>>>>   *             updateMessage("Cancelled!");
>>>>>   *         }
>>>>>   *
>>>>>   *         @@Override protected void failed() {
>>>>>   *             super.failed();
>>>>>   *             updateMessage("Failed!");
>>>>>   *         }
>>>>>   *     }
>>>>>   *
>>>>> >>>>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>>>> >>>>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>>>> >>>>> Thanks everybody for your very valuable input! >>>>> Richard >>>>> >>>>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>>>> >>>>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>>>> >>>>>> Steve >>>>>> >>>>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>>>> Hi Richard, >>>>>>> >>>>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>>>> faulty code. >>>>>>> >>>>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>>>> especially the FutureTask class. I.e. provide an API to check >>>>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>>>> interrupt a thread: >>>>>>> >>>>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>>>> >>>>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>>>> of shitty code being written (surprise!), especially when threads are >>>>>>> involved. >>>>>>> >>>>>>> Cheers, Roman >>>>>>> >>>>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>>>> >>>>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>>>> >>>>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>>>> >>>>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>>>> >>>>>>>> If you write a Task, and do nothing smart: >>>>>>>> >>>>>>>> new Task() { >>>>>>>> public Object call() throws Exception { >>>>>>>> for (int i=0; i<100; i++) { >>>>>>>> doSomething(); >>>>>>>> } >>>>>>>> return null; >>>>>>>> } >>>>>>>> }; >>>>>>>> >>>>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>>>> >>>>>>>> new Task() { >>>>>>>> public Object call() throws Exception { >>>>>>>> for (int i=0; i<100; i++) { >>>>>>>> doSomething(); >>>>>>>> updateProgress(i, 100); >>>>>>>> } >>>>>>>> return null; >>>>>>>> } >>>>>>>> }; >>>>>>>> >>>>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>>>> >>>>>>>> In this case...: >>>>>>>> >>>>>>>> new Task() { >>>>>>>> public Object call() throws Exception { >>>>>>>> for (int i=0; i<100; i++) { >>>>>>>> try { >>>>>>>> doSomething(); >>>>>>>> updateProgress(i, 100); >>>>>>>> } catch (Exception e) { } >>>>>>>> } >>>>>>>> return null; >>>>>>>> } >>>>>>>> }; >>>>>>>> >>>>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>>>> >>>>>>>> new Task() { >>>>>>>> public Object call() throws Exception { >>>>>>>> for (int i=0; i<100; i++) { >>>>>>>> if (isCancelled()) break; >>>>>>>> try { >>>>>>>> doSomething(); >>>>>>>> updateProgress(i, 100); >>>>>>>> } catch (Exception e) { } >>>>>>>> } >>>>>>>> return null; >>>>>>>> } >>>>>>>> }; >>>>>>>> >>>>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>>>> >>>>>>>> new Task() { >>>>>>>> public Object call() throws Exception { >>>>>>>> for (int i=0; i<100; i++) { >>>>>>>> if (isCancelled()) { >>>>>>>> Platform.runLater(new Runnable() { >>>>>>>> public void run() { >>>>>>>> updateMessage("Cancelled") >>>>>>>> } >>>>>>>> }); >>>>>>>> break; >>>>>>>> } >>>>>>>> try { >>>>>>>> doSomething(); >>>>>>>> updateProgress(i, 100); >>>>>>>> } catch (Exception e) { } >>>>>>>> } >>>>>>>> return null; >>>>>>>> } >>>>>>>> }; >>>>>>>> >>>>>>>> new Task() { >>>>>>>> public Object call() throws Exception { >>>>>>>> for (int i=0; i<100; i++) { >>>>>>>> if (isCancelled()) break; >>>>>>>> try { >>>>>>>> doSomething(); >>>>>>>> updateProgress(i, 100); >>>>>>>> } catch (Exception e) { } >>>>>>>> } >>>>>>>> return null; >>>>>>>> } >>>>>>>> >>>>>>>> @Override protected void cancelled() { >>>>>>>> updateMessage("Cancelled"); >>>>>>>> } >>>>>>>> }; >>>>>>>> >>>>>>>> I think this works fairly well. Anything I'm missing? >>>>>>>> >>>>>>>> Thanks! >>>>>>>> Richard From Richard.Bair at oracle.com Wed Jan 4 11:30:08 2012 From: Richard.Bair at oracle.com (Richard Bair) Date: Wed, 4 Jan 2012 11:30:08 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F04A758.8090404@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> Message-ID: What do you mean? On Jan 4, 2012, at 11:24 AM, steve.x.northover at oracle.com wrote: > One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. > > Steve > > On 04/01/2012 2:11 PM, Richard Bair wrote: >> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >> >> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >> >> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >> >> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >> >> Richard >> >> >> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >> >>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>> >>> Steve >>> >>> On 04/01/2012 2:00 PM, Richard Bair wrote: >>>> Today, they just work (they update the progress, title, message). >>>> >>>> Richard >>>> >>>> On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: >>>> >>>>> What happens now when updateProgress() and friends are called for a canceled task? >>>>> >>>>> Steve >>>>> >>>>> On 04/01/2012 1:55 PM, Richard Bair wrote: >>>>>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>>>>> >>>>>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>>>>> >>>>>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>>>>> >>>>>> *

Examples

>>>>>> *

>>>>>> * The following set of examples demonstrate some of the most common uses of >>>>>> * Tasks. >>>>>> *

>>>>>> * >>>>>> *

A Simple Loop

>>>>>> * >>>>>> *

>>>>>> * The first example is a simple loop that does nothing particularly useful, >>>>>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>>>>> * example will simply loop and print to standard out on each loop iteration. >>>>>> * When it completes, it returns the number of times it iterated. >>>>>> *

>>>>>> * >>>>>> *

>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>  *             int iterations = 0;
>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>  *                 if (isCancelled()) {
>>>>>>  *                     break;
>>>>>>  *                 }
>>>>>>  *                 System.out.println("Iteration " + iterations);
>>>>>>  *             }
>>>>>>  *             return iterations;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

>>>>>> * First, we define what type of value is returned from this Task. In this >>>>>> * case, we want to return the number of times we iterated, so we will >>>>>> * specify the Task to be of type Integer by using generics. Then, within >>>>>> * the implementation of thecall method, we iterate from >>>>>> * 0 to 1000. On each iteration, we check to see whether this Task has >>>>>> * been cancelled. If it has been, then we break out of the loop and return >>>>>> * the number of times we iterated. Otherwise a message is printed to >>>>>> * the console and the iteration count increased and we continue looping. >>>>>> *

>>>>>> * >>>>>> *

>>>>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>>>>> * developer may cancel the task, but the task will continue running >>>>>> * and updating both the progress and returning the incorrect result >>>>>> * from the end of thecall method. A correct implementation >>>>>> * of a Task will always check for cancellation. >>>>>> *

>>>>>> * >>>>>> *

A Simple Loop With Progress Notification

>>>>>> * >>>>>> *

>>>>>> * Similar to the previous example, except this time we will modify the >>>>>> * progress of the Task in each iteration. Note that we have a choice >>>>>> * to make in the case of cancellation. Do we want to set the progress back >>>>>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>>>>> * the progress where it was at? In this case, lets leave the progress alone >>>>>> * and only update the message on cancellation, though updating the >>>>>> * progress after cancellation is a perfectly valid choice. >>>>>> *

>>>>>> * >>>>>> *

>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>  *             int iterations = 0;
>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>  *                 if (isCancelled()) {
>>>>>>  *                     updateMessage("Cancelled");
>>>>>>  *                     break;
>>>>>>  *                 }
>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>  *                 (iterations, 1000);
>>>>>>  *             }
>>>>>>  *             return iterations;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

>>>>>> * As before, within the for loop we check whether the Task has been >>>>>> * cancelled. If it has been cancelled, we will update the Task's >>>>>> * message to indicate that it has been cancelled, and then break as >>>>>> * before. If the Task has not been cancelled, then we will update its >>>>>> * message to indicate the current iteration and then update the >>>>>> * progress to indicate the current progress. >>>>>> *

>>>>>> * >>>>>> *

A Simple Loop With Progress Notification And Blocking Calls

>>>>>> * >>>>>> *

>>>>>> * This example adds to the previous examples a blocking call. Because a >>>>>> * blocking call may thrown an InterruptedException, and because an >>>>>> * InterruptedException may occur as a result of the Task being cancelled, >>>>>> * we need to be sure to handle the InterruptedException and check on the >>>>>> * cancel state. >>>>>> *

>>>>>> * >>>>>> *

>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>  *             int iterations = 0;
>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>  *                 if (isCancelled()) {
>>>>>>  *                     updateMessage("Cancelled");
>>>>>>  *                     break;
>>>>>>  *                 }
>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>  *                 updateProgress(iterations, 1000);
>>>>>>  *
>>>>>>  *                 // Now block the thread for a short time, but be sure
>>>>>>  *                 // to check the interrupted exception for cancellation!
>>>>>>  *                 try {
>>>>>>  *                     Thread.sleep(100);
>>>>>>  *                 } catch (InterruptedException interrupted) {
>>>>>>  *                     if (isCancelled()) {
>>>>>>  *                         updateMessage("Cancelled");
>>>>>>  *                         break;
>>>>>>  *                     }
>>>>>>  *                 }
>>>>>>  *             }
>>>>>>  *             return iterations;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

>>>>>> * Here we have added to the body of the loop aThread.sleep >>>>>> * call. Since this is a blocking call, I have to handle the potential >>>>>> * InterruptedException. Within the catch block, I will check whether >>>>>> * the Task has been cancelled, and if so, update the message accordingly >>>>>> * and break out of the loop. >>>>>> *

>>>>>> * >>>>>> *

A Task Which Takes Parameters

>>>>>> * >>>>>> *

>>>>>> * Most Tasks require some parameters in order to do useful work. For >>>>>> * example, a DeleteRecordTask needs the object or primary key to delete >>>>>> * from the database. A ReadFileTask needs the URI of the file to be read. >>>>>> * Because Tasks operate on a background thread, care must be taken to >>>>>> * make sure the body of thecall method does not read or >>>>>> * modify any shared state. There are two techniques most useful for >>>>>> * doing this: using final variables, and passing variables to a Task >>>>>> * during construction. >>>>>> *

>>>>>> * >>>>>> *

>>>>>> * When using a Task as an anonymous class, the most natural way to pass >>>>>> * parameters to the Task is by using final variables. In this example, >>>>>> * we pass to the Task the total number of times the Task should iterate. >>>>>> *

>>>>>> * >>>>>> *

>>>>>>  *     final int totalIterations = 900;
>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>  *             int iterations = 0;
>>>>>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>  *                 if (isCancelled()) {
>>>>>>  *                     updateMessage("Cancelled");
>>>>>>  *                     break;
>>>>>>  *                 }
>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>  *                 updateProgress(iterations, totalIterations);
>>>>>>  *             }
>>>>>>  *             return iterations;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

>>>>>> * SincetotalIterations is final, thecall >>>>>> * method can safely read it and refer to it from a background thread. >>>>>> *

>>>>>> * >>>>>> *

>>>>>> * When writing Task libraries (as opposed to specific-use implementations), >>>>>> * we need to use a different technique. In this case, I will create an >>>>>> * IteratingTask which performs the same work as above. This time, since >>>>>> * the IteratingTask is defined in its own file, it will need to have >>>>>> * parameters passed to it in its constructor. These parameters are >>>>>> * assigned to final variables. >>>>>> *

>>>>>> * >>>>>> *

>>>>>>  *     public class IteratingTask extends Task<Integer> {
>>>>>>  *         private final int totalIterations;
>>>>>>  *
>>>>>>  *         public IteratingTask(int totalIterations) {
>>>>>>  *             this.totalIterations = totalIterations;
>>>>>>  *         }
>>>>>>  *
>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>  *             int iterations = 0;
>>>>>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>  *                 if (isCancelled()) {
>>>>>>  *                     updateMessage("Cancelled");
>>>>>>  *                     break;
>>>>>>  *                 }
>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>  *                 updateProgress(iterations, totalIterations);
>>>>>>  *             }
>>>>>>  *             return iterations;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

And then when used:

>>>>>> * >>>>>> *

>>>>>>  *     IteratingTask task = new IteratingTask(800);
>>>>>>  *
>>>>>> * >>>>>> *

In this way, parameters are passed to the IteratingTask in a safe >>>>>> * manner, and again, are final. Thus, thecall method can >>>>>> * safely read this state from a background thread.

>>>>>> * >>>>>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>>>>> * from a background thread. Doing so may introduce race conditions. In >>>>>> * particular, suppose you had a SaveCustomerTask which took a Customer >>>>>> * in its constructor. Although the SaveCustomerTask may have a final >>>>>> * reference to the Customer, if the Customer object is mutable, then it >>>>>> * is possible that both the SaveCustomerTask and some other application code >>>>>> * will be reading or modifying the state of the Customer from different >>>>>> * threads. Be very careful in such cases, that while a mutable object such >>>>>> * as this Customer is being used from a background thread, that it is >>>>>> * not being used also from another thread. In particular, if the background >>>>>> * thread is reading data from the database and updating the Customer object, >>>>>> * and the Customer object is bound to scene graph nodes (such as UI >>>>>> * controls), then there could be a violation of threading rules! For such >>>>>> * cases, modify the Customer object from the FX Application Thread rather >>>>>> * than from the background thread.

>>>>>> * >>>>>> *

>>>>>>  *     public class UpdateCustomerTask extends Task<Customer> {
>>>>>>  *         private final Customer customer;
>>>>>>  *
>>>>>>  *         public UpdateCustomerTask(Customer customer) {
>>>>>>  *             this.customer = customer;
>>>>>>  *         }
>>>>>>  *
>>>>>>  *         @@Override protected Customer call() throws Exception {
>>>>>>  *             // pseudo-code:
>>>>>>  *             //   query the database
>>>>>>  *             //   read the values
>>>>>>  *
>>>>>>  *             // Now update the customer
>>>>>>  *             Platform.runLater(new Runnable() {
>>>>>>  *                 @@Override public void run() {
>>>>>>  *                     customer.firstName(rs.getString("FirstName"));
>>>>>>  *                     // etc
>>>>>>  *                 }
>>>>>>  *             });
>>>>>>  *
>>>>>>  *             return customer;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

A Task Which Returns No Value

>>>>>> * >>>>>> *

>>>>>> * Many, if not most, Tasks should return a value upon completion. For >>>>>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>>>>> * created object or primary key, a "Read" Task would return the read >>>>>> * object, an "Update" task would return the number of records updated, >>>>>> * and a "Delete" task would return the number of records deleted. >>>>>> *

>>>>>> * >>>>>> *

>>>>>> * However sometimes there just isn't anything truly useful to return. >>>>>> * For example, I might have a Task which writes to a file. Task has built >>>>>> * into it a mechanism for indicating whether it has succeeded or failed >>>>>> * along with the number of bytes written (the progress), and thus there is >>>>>> * nothing really for me to return. In such a case, you can use the Void >>>>>> * type. This is a special type in the Java language which can only be >>>>>> * assigned the value ofnull. You would use it as follows: >>>>>> *

>>>>>> * >>>>>> *

>>>>>>  *     final String filePath = "/foo.txt";
>>>>>>  *     final String contents = "Some contents";
>>>>>>  *     Task<Void> task = new Task<Void>() {
>>>>>>  *         @@Override protected Void call() throws Exception {
>>>>>>  *             File file = new File(filePath);
>>>>>>  *             FileOutputStream out = new FileOutputStream(file);
>>>>>>  *             // ... and other code to write the contents ...
>>>>>>  *
>>>>>>  *             // Return null at the end of a Task of type Void
>>>>>>  *             return null;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

A Task Which Returns An ObservableList

>>>>>> * >>>>>> *

Because the ListView, TableView, and other UI controls and scene graph >>>>>> * nodes make use of ObservableList, it is common to want to create and return >>>>>> * an ObservableList from a Task. When you do not care to display intermediate >>>>>> * values, the easiest way to correctly write such a Task is simply to >>>>>> * construct an ObservableList within thecall method, and then >>>>>> * return it at the conclusion of the Task.

>>>>>> * >>>>>> *

>>>>>>  *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>>>>  *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>>>>  *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>>>>  *             for (int i=0; i<100; i++) {
>>>>>>  *                 if (isCancelled()) break;
>>>>>>  *                 Rectangle r = new Rectangle(10, 10);
>>>>>>  *                 r.setX(10 * i);
>>>>>>  *                 results.add(r);
>>>>>>  *             }
>>>>>>  *             return results;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

In the above example, we are going to create 100 rectangles and return >>>>>> * them from this task. An ObservableList is created within the >>>>>> *call method, populated, and then returned.

>>>>>> * >>>>>> *

A Task Which Returns Partial Results

>>>>>> * >>>>>> *

Sometimes you want to create a Task which will return partial results. >>>>>> * Perhaps you are building a complex scene graph and want to show the >>>>>> * scene graph as it is being constructed. Or perhaps you are reading a large >>>>>> * amount of data over the network and want to display the entries in a >>>>>> * TableView as the data is arriving. In such cases, there is some shared state >>>>>> * available both to the FX Application Thread and the background thread. >>>>>> * Great care must be taken tonever update shared state from any >>>>>> * thread other than the FX Application Thread.

>>>>>> * >>>>>> *

The easiest way to do this is to expose a new property on the Task >>>>>> * which will represent the partial result. Then make sure to use >>>>>> *Platform.runLater when adding new items to the partial >>>>>> * result.

>>>>>> * >>>>>> *

>>>>>>  *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>>>>  *         // Uses Java 7 diamond operator
>>>>>>  *         private ReadOnlyObjectWrapper>    partialResults =
>>>>>>  *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>>>>  *
>>>>>>  *         public final ObservableList    getPartialResults() { return partialResults; }
>>>>>>  *         public final ReadOnlyObjectProperty>    partialResultsProperty() {
>>>>>>  *             return partialResults.getReadOnlyProperty();
>>>>>>  *         }
>>>>>>  *
>>>>>>  *         @@Override protected ObservableList    call() throws Exception {
>>>>>>  *             for (int i=0; i<100; i++) {
>>>>>>  *                 if (isCancelled()) break;
>>>>>>  *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>  *                 r.setX(10 * i);
>>>>>>  *                 Platform.runLater(new Runnable() {
>>>>>>  *                     @@Override public void run() {
>>>>>>  *                         partialResults.add(r);
>>>>>>  *                     }
>>>>>>  *                 });
>>>>>>  *             }
>>>>>>  *             return partialResults;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> * >>>>>> * >>>>>> *

A Task Which Modifies The Scene Graph

>>>>>> * >>>>>> *

Generally, Tasks should not interact directly with the UI. Doing so >>>>>> * creates a tight coupling between a specific Task implementation and a >>>>>> * specific part of your UI. However, when you do want to create such a >>>>>> * coupling, you must ensure that you usePlatform.runLater >>>>>> * so that any modifications of the scene graph occur on the >>>>>> * FX Application Thread.

>>>>>> * >>>>>> *

>>>>>>  *     final Group group = new Group();
>>>>>>  *     Task<Void> task = new Task<Void>() {
>>>>>>  *         @@Override protected Void call() throws Exception {
>>>>>>  *             for (int i=0; i<100; i++) {
>>>>>>  *                 if (isCancelled()) break;
>>>>>>  *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>  *                 r.setX(10 * i);
>>>>>>  *                 Platform.runLater(new Runnable() {
>>>>>>  *                     @@Override public void run() {
>>>>>>  *                         group.getChildren().add(r);
>>>>>>  *                     }
>>>>>>  *                 });
>>>>>>  *             }
>>>>>>  *             return null;
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> * >>>>>> *

Reacting To State Changes Generically

>>>>>> * >>>>>> *

Sometimes you may want to write a Task which updates its progress, >>>>>> * message, text, or in some other way reacts whenever a state change >>>>>> * happens on the Task. For example, you may want to change the status >>>>>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>>>>> *

>>>>>> *

>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>  *             int iterations = 0;
>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>  *                 if (isCancelled()) {
>>>>>>  *                     break;
>>>>>>  *                 }
>>>>>>  *                 System.out.println("Iteration " + iterations);
>>>>>>  *             }
>>>>>>  *             return iterations;
>>>>>>  *         }
>>>>>>  *
>>>>>>  *         @@Override protected void succeeded() {
>>>>>>  *             super.succeeded();
>>>>>>  *             updateMessage("Done!");
>>>>>>  *         }
>>>>>>  *
>>>>>>  *         @@Override protected void cancelled() {
>>>>>>  *             super.cancelled();
>>>>>>  *             updateMessage("Cancelled!");
>>>>>>  *         }
>>>>>>  *
>>>>>>  *         @@Override protected void failed() {
>>>>>>  *             super.failed();
>>>>>>  *             updateMessage("Failed!");
>>>>>>  *         }
>>>>>>  *     }
>>>>>>  *
>>>>>> >>>>>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>>>>> >>>>>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>>>>> >>>>>> Thanks everybody for your very valuable input! >>>>>> Richard >>>>>> >>>>>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>>>>> >>>>>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>>>>> >>>>>>> Steve >>>>>>> >>>>>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>>>>> Hi Richard, >>>>>>>> >>>>>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>>>>> faulty code. >>>>>>>> >>>>>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>>>>> especially the FutureTask class. I.e. provide an API to check >>>>>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>>>>> interrupt a thread: >>>>>>>> >>>>>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>>>>> >>>>>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>>>>> of shitty code being written (surprise!), especially when threads are >>>>>>>> involved. >>>>>>>> >>>>>>>> Cheers, Roman >>>>>>>> >>>>>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>>>>> >>>>>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>>>>> >>>>>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>>>>> >>>>>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>>>>> >>>>>>>>> If you write a Task, and do nothing smart: >>>>>>>>> >>>>>>>>> new Task() { >>>>>>>>> public Object call() throws Exception { >>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>> doSomething(); >>>>>>>>> } >>>>>>>>> return null; >>>>>>>>> } >>>>>>>>> }; >>>>>>>>> >>>>>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>>>>> >>>>>>>>> new Task() { >>>>>>>>> public Object call() throws Exception { >>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>> doSomething(); >>>>>>>>> updateProgress(i, 100); >>>>>>>>> } >>>>>>>>> return null; >>>>>>>>> } >>>>>>>>> }; >>>>>>>>> >>>>>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>>>>> >>>>>>>>> In this case...: >>>>>>>>> >>>>>>>>> new Task() { >>>>>>>>> public Object call() throws Exception { >>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>> try { >>>>>>>>> doSomething(); >>>>>>>>> updateProgress(i, 100); >>>>>>>>> } catch (Exception e) { } >>>>>>>>> } >>>>>>>>> return null; >>>>>>>>> } >>>>>>>>> }; >>>>>>>>> >>>>>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>>>>> >>>>>>>>> new Task() { >>>>>>>>> public Object call() throws Exception { >>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>> if (isCancelled()) break; >>>>>>>>> try { >>>>>>>>> doSomething(); >>>>>>>>> updateProgress(i, 100); >>>>>>>>> } catch (Exception e) { } >>>>>>>>> } >>>>>>>>> return null; >>>>>>>>> } >>>>>>>>> }; >>>>>>>>> >>>>>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>>>>> >>>>>>>>> new Task() { >>>>>>>>> public Object call() throws Exception { >>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>> if (isCancelled()) { >>>>>>>>> Platform.runLater(new Runnable() { >>>>>>>>> public void run() { >>>>>>>>> updateMessage("Cancelled") >>>>>>>>> } >>>>>>>>> }); >>>>>>>>> break; >>>>>>>>> } >>>>>>>>> try { >>>>>>>>> doSomething(); >>>>>>>>> updateProgress(i, 100); >>>>>>>>> } catch (Exception e) { } >>>>>>>>> } >>>>>>>>> return null; >>>>>>>>> } >>>>>>>>> }; >>>>>>>>> >>>>>>>>> new Task() { >>>>>>>>> public Object call() throws Exception { >>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>> if (isCancelled()) break; >>>>>>>>> try { >>>>>>>>> doSomething(); >>>>>>>>> updateProgress(i, 100); >>>>>>>>> } catch (Exception e) { } >>>>>>>>> } >>>>>>>>> return null; >>>>>>>>> } >>>>>>>>> >>>>>>>>> @Override protected void cancelled() { >>>>>>>>> updateMessage("Cancelled"); >>>>>>>>> } >>>>>>>>> }; >>>>>>>>> >>>>>>>>> I think this works fairly well. Anything I'm missing? >>>>>>>>> >>>>>>>>> Thanks! >>>>>>>>> Richard From steve.x.northover at oracle.com Wed Jan 4 11:37:35 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Wed, 04 Jan 2012 14:37:35 -0500 Subject: Review of solution for Cancelled Tasks In-Reply-To: References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> Message-ID: <4F04AA7F.1050105@oracle.com> for (iterations=0; iterations<1000; iterations++) } //ugly API but means that you can't update progress without checking for cancel or setting the message if (isCancelled("Interation " + i, i) { break; } ... do work ... } Steve On 04/01/2012 2:30 PM, Richard Bair wrote: > What do you mean? > > On Jan 4, 2012, at 11:24 AM, steve.x.northover at oracle.com wrote: > >> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >> >> Steve >> >> On 04/01/2012 2:11 PM, Richard Bair wrote: >>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>> >>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>> >>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>> >>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>> >>> Richard >>> >>> >>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>> >>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>> >>>> Steve >>>> >>>> On 04/01/2012 2:00 PM, Richard Bair wrote: >>>>> Today, they just work (they update the progress, title, message). >>>>> >>>>> Richard >>>>> >>>>> On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: >>>>> >>>>>> What happens now when updateProgress() and friends are called for a canceled task? >>>>>> >>>>>> Steve >>>>>> >>>>>> On 04/01/2012 1:55 PM, Richard Bair wrote: >>>>>>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>>>>>> >>>>>>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>>>>>> >>>>>>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>>>>>> >>>>>>> *

Examples

>>>>>>> *

>>>>>>> * The following set of examples demonstrate some of the most common uses of >>>>>>> * Tasks. >>>>>>> *

>>>>>>> * >>>>>>> *

A Simple Loop

>>>>>>> * >>>>>>> *

>>>>>>> * The first example is a simple loop that does nothing particularly useful, >>>>>>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>>>>>> * example will simply loop and print to standard out on each loop iteration. >>>>>>> * When it completes, it returns the number of times it iterated. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>>>   *             int iterations = 0;
>>>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>   *                 if (isCancelled()) {
>>>>>>>   *                     break;
>>>>>>>   *                 }
>>>>>>>   *                 System.out.println("Iteration " + iterations);
>>>>>>>   *             }
>>>>>>>   *             return iterations;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

>>>>>>> * First, we define what type of value is returned from this Task. In this >>>>>>> * case, we want to return the number of times we iterated, so we will >>>>>>> * specify the Task to be of type Integer by using generics. Then, within >>>>>>> * the implementation of thecall method, we iterate from >>>>>>> * 0 to 1000. On each iteration, we check to see whether this Task has >>>>>>> * been cancelled. If it has been, then we break out of the loop and return >>>>>>> * the number of times we iterated. Otherwise a message is printed to >>>>>>> * the console and the iteration count increased and we continue looping. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>>>>>> * developer may cancel the task, but the task will continue running >>>>>>> * and updating both the progress and returning the incorrect result >>>>>>> * from the end of thecall method. A correct implementation >>>>>>> * of a Task will always check for cancellation. >>>>>>> *

>>>>>>> * >>>>>>> *

A Simple Loop With Progress Notification

>>>>>>> * >>>>>>> *

>>>>>>> * Similar to the previous example, except this time we will modify the >>>>>>> * progress of the Task in each iteration. Note that we have a choice >>>>>>> * to make in the case of cancellation. Do we want to set the progress back >>>>>>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>>>>>> * the progress where it was at? In this case, lets leave the progress alone >>>>>>> * and only update the message on cancellation, though updating the >>>>>>> * progress after cancellation is a perfectly valid choice. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>>>   *             int iterations = 0;
>>>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>   *                 if (isCancelled()) {
>>>>>>>   *                     updateMessage("Cancelled");
>>>>>>>   *                     break;
>>>>>>>   *                 }
>>>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>>>   *                 (iterations, 1000);
>>>>>>>   *             }
>>>>>>>   *             return iterations;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

>>>>>>> * As before, within the for loop we check whether the Task has been >>>>>>> * cancelled. If it has been cancelled, we will update the Task's >>>>>>> * message to indicate that it has been cancelled, and then break as >>>>>>> * before. If the Task has not been cancelled, then we will update its >>>>>>> * message to indicate the current iteration and then update the >>>>>>> * progress to indicate the current progress. >>>>>>> *

>>>>>>> * >>>>>>> *

A Simple Loop With Progress Notification And Blocking Calls

>>>>>>> * >>>>>>> *

>>>>>>> * This example adds to the previous examples a blocking call. Because a >>>>>>> * blocking call may thrown an InterruptedException, and because an >>>>>>> * InterruptedException may occur as a result of the Task being cancelled, >>>>>>> * we need to be sure to handle the InterruptedException and check on the >>>>>>> * cancel state. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>>>   *             int iterations = 0;
>>>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>   *                 if (isCancelled()) {
>>>>>>>   *                     updateMessage("Cancelled");
>>>>>>>   *                     break;
>>>>>>>   *                 }
>>>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>>>   *                 updateProgress(iterations, 1000);
>>>>>>>   *
>>>>>>>   *                 // Now block the thread for a short time, but be sure
>>>>>>>   *                 // to check the interrupted exception for cancellation!
>>>>>>>   *                 try {
>>>>>>>   *                     Thread.sleep(100);
>>>>>>>   *                 } catch (InterruptedException interrupted) {
>>>>>>>   *                     if (isCancelled()) {
>>>>>>>   *                         updateMessage("Cancelled");
>>>>>>>   *                         break;
>>>>>>>   *                     }
>>>>>>>   *                 }
>>>>>>>   *             }
>>>>>>>   *             return iterations;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

>>>>>>> * Here we have added to the body of the loop aThread.sleep >>>>>>> * call. Since this is a blocking call, I have to handle the potential >>>>>>> * InterruptedException. Within the catch block, I will check whether >>>>>>> * the Task has been cancelled, and if so, update the message accordingly >>>>>>> * and break out of the loop. >>>>>>> *

>>>>>>> * >>>>>>> *

A Task Which Takes Parameters

>>>>>>> * >>>>>>> *

>>>>>>> * Most Tasks require some parameters in order to do useful work. For >>>>>>> * example, a DeleteRecordTask needs the object or primary key to delete >>>>>>> * from the database. A ReadFileTask needs the URI of the file to be read. >>>>>>> * Because Tasks operate on a background thread, care must be taken to >>>>>>> * make sure the body of thecall method does not read or >>>>>>> * modify any shared state. There are two techniques most useful for >>>>>>> * doing this: using final variables, and passing variables to a Task >>>>>>> * during construction. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>> * When using a Task as an anonymous class, the most natural way to pass >>>>>>> * parameters to the Task is by using final variables. In this example, >>>>>>> * we pass to the Task the total number of times the Task should iterate. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>>   *     final int totalIterations = 900;
>>>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>>>   *             int iterations = 0;
>>>>>>>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>   *                 if (isCancelled()) {
>>>>>>>   *                     updateMessage("Cancelled");
>>>>>>>   *                     break;
>>>>>>>   *                 }
>>>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>>>   *                 updateProgress(iterations, totalIterations);
>>>>>>>   *             }
>>>>>>>   *             return iterations;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

>>>>>>> * SincetotalIterations is final, thecall >>>>>>> * method can safely read it and refer to it from a background thread. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>> * When writing Task libraries (as opposed to specific-use implementations), >>>>>>> * we need to use a different technique. In this case, I will create an >>>>>>> * IteratingTask which performs the same work as above. This time, since >>>>>>> * the IteratingTask is defined in its own file, it will need to have >>>>>>> * parameters passed to it in its constructor. These parameters are >>>>>>> * assigned to final variables. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>>   *     public class IteratingTask extends Task<Integer> {
>>>>>>>   *         private final int totalIterations;
>>>>>>>   *
>>>>>>>   *         public IteratingTask(int totalIterations) {
>>>>>>>   *             this.totalIterations = totalIterations;
>>>>>>>   *         }
>>>>>>>   *
>>>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>>>   *             int iterations = 0;
>>>>>>>   *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>   *                 if (isCancelled()) {
>>>>>>>   *                     updateMessage("Cancelled");
>>>>>>>   *                     break;
>>>>>>>   *                 }
>>>>>>>   *                 updateMessage("Iteration " + iterations);
>>>>>>>   *                 updateProgress(iterations, totalIterations);
>>>>>>>   *             }
>>>>>>>   *             return iterations;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

And then when used:

>>>>>>> * >>>>>>> *

>>>>>>>   *     IteratingTask task = new IteratingTask(800);
>>>>>>>   *
>>>>>>> * >>>>>>> *

In this way, parameters are passed to the IteratingTask in a safe >>>>>>> * manner, and again, are final. Thus, thecall method can >>>>>>> * safely read this state from a background thread.

>>>>>>> * >>>>>>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>>>>>> * from a background thread. Doing so may introduce race conditions. In >>>>>>> * particular, suppose you had a SaveCustomerTask which took a Customer >>>>>>> * in its constructor. Although the SaveCustomerTask may have a final >>>>>>> * reference to the Customer, if the Customer object is mutable, then it >>>>>>> * is possible that both the SaveCustomerTask and some other application code >>>>>>> * will be reading or modifying the state of the Customer from different >>>>>>> * threads. Be very careful in such cases, that while a mutable object such >>>>>>> * as this Customer is being used from a background thread, that it is >>>>>>> * not being used also from another thread. In particular, if the background >>>>>>> * thread is reading data from the database and updating the Customer object, >>>>>>> * and the Customer object is bound to scene graph nodes (such as UI >>>>>>> * controls), then there could be a violation of threading rules! For such >>>>>>> * cases, modify the Customer object from the FX Application Thread rather >>>>>>> * than from the background thread.

>>>>>>> * >>>>>>> *

>>>>>>>   *     public class UpdateCustomerTask extends Task<Customer> {
>>>>>>>   *         private final Customer customer;
>>>>>>>   *
>>>>>>>   *         public UpdateCustomerTask(Customer customer) {
>>>>>>>   *             this.customer = customer;
>>>>>>>   *         }
>>>>>>>   *
>>>>>>>   *         @@Override protected Customer call() throws Exception {
>>>>>>>   *             // pseudo-code:
>>>>>>>   *             //   query the database
>>>>>>>   *             //   read the values
>>>>>>>   *
>>>>>>>   *             // Now update the customer
>>>>>>>   *             Platform.runLater(new Runnable() {
>>>>>>>   *                 @@Override public void run() {
>>>>>>>   *                     customer.firstName(rs.getString("FirstName"));
>>>>>>>   *                     // etc
>>>>>>>   *                 }
>>>>>>>   *             });
>>>>>>>   *
>>>>>>>   *             return customer;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

A Task Which Returns No Value

>>>>>>> * >>>>>>> *

>>>>>>> * Many, if not most, Tasks should return a value upon completion. For >>>>>>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>>>>>> * created object or primary key, a "Read" Task would return the read >>>>>>> * object, an "Update" task would return the number of records updated, >>>>>>> * and a "Delete" task would return the number of records deleted. >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>> * However sometimes there just isn't anything truly useful to return. >>>>>>> * For example, I might have a Task which writes to a file. Task has built >>>>>>> * into it a mechanism for indicating whether it has succeeded or failed >>>>>>> * along with the number of bytes written (the progress), and thus there is >>>>>>> * nothing really for me to return. In such a case, you can use the Void >>>>>>> * type. This is a special type in the Java language which can only be >>>>>>> * assigned the value ofnull. You would use it as follows: >>>>>>> *

>>>>>>> * >>>>>>> *

>>>>>>>   *     final String filePath = "/foo.txt";
>>>>>>>   *     final String contents = "Some contents";
>>>>>>>   *     Task<Void> task = new Task<Void>() {
>>>>>>>   *         @@Override protected Void call() throws Exception {
>>>>>>>   *             File file = new File(filePath);
>>>>>>>   *             FileOutputStream out = new FileOutputStream(file);
>>>>>>>   *             // ... and other code to write the contents ...
>>>>>>>   *
>>>>>>>   *             // Return null at the end of a Task of type Void
>>>>>>>   *             return null;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

A Task Which Returns An ObservableList

>>>>>>> * >>>>>>> *

Because the ListView, TableView, and other UI controls and scene graph >>>>>>> * nodes make use of ObservableList, it is common to want to create and return >>>>>>> * an ObservableList from a Task. When you do not care to display intermediate >>>>>>> * values, the easiest way to correctly write such a Task is simply to >>>>>>> * construct an ObservableList within thecall method, and then >>>>>>> * return it at the conclusion of the Task.

>>>>>>> * >>>>>>> *

>>>>>>>   *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>>>>>   *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>>>>>   *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>>>>>   *             for (int i=0; i<100; i++) {
>>>>>>>   *                 if (isCancelled()) break;
>>>>>>>   *                 Rectangle r = new Rectangle(10, 10);
>>>>>>>   *                 r.setX(10 * i);
>>>>>>>   *                 results.add(r);
>>>>>>>   *             }
>>>>>>>   *             return results;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

In the above example, we are going to create 100 rectangles and return >>>>>>> * them from this task. An ObservableList is created within the >>>>>>> *call method, populated, and then returned.

>>>>>>> * >>>>>>> *

A Task Which Returns Partial Results

>>>>>>> * >>>>>>> *

Sometimes you want to create a Task which will return partial results. >>>>>>> * Perhaps you are building a complex scene graph and want to show the >>>>>>> * scene graph as it is being constructed. Or perhaps you are reading a large >>>>>>> * amount of data over the network and want to display the entries in a >>>>>>> * TableView as the data is arriving. In such cases, there is some shared state >>>>>>> * available both to the FX Application Thread and the background thread. >>>>>>> * Great care must be taken tonever update shared state from any >>>>>>> * thread other than the FX Application Thread.

>>>>>>> * >>>>>>> *

The easiest way to do this is to expose a new property on the Task >>>>>>> * which will represent the partial result. Then make sure to use >>>>>>> *Platform.runLater when adding new items to the partial >>>>>>> * result.

>>>>>>> * >>>>>>> *

>>>>>>>   *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>>>>>   *         // Uses Java 7 diamond operator
>>>>>>>   *         private ReadOnlyObjectWrapper>     partialResults =
>>>>>>>   *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>>>>>   *
>>>>>>>   *         public final ObservableList     getPartialResults() { return partialResults; }
>>>>>>>   *         public final ReadOnlyObjectProperty>     partialResultsProperty() {
>>>>>>>   *             return partialResults.getReadOnlyProperty();
>>>>>>>   *         }
>>>>>>>   *
>>>>>>>   *         @@Override protected ObservableList     call() throws Exception {
>>>>>>>   *             for (int i=0; i<100; i++) {
>>>>>>>   *                 if (isCancelled()) break;
>>>>>>>   *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>   *                 r.setX(10 * i);
>>>>>>>   *                 Platform.runLater(new Runnable() {
>>>>>>>   *                     @@Override public void run() {
>>>>>>>   *                         partialResults.add(r);
>>>>>>>   *                     }
>>>>>>>   *                 });
>>>>>>>   *             }
>>>>>>>   *             return partialResults;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> * >>>>>>> * >>>>>>> *

A Task Which Modifies The Scene Graph

>>>>>>> * >>>>>>> *

Generally, Tasks should not interact directly with the UI. Doing so >>>>>>> * creates a tight coupling between a specific Task implementation and a >>>>>>> * specific part of your UI. However, when you do want to create such a >>>>>>> * coupling, you must ensure that you usePlatform.runLater >>>>>>> * so that any modifications of the scene graph occur on the >>>>>>> * FX Application Thread.

>>>>>>> * >>>>>>> *

>>>>>>>   *     final Group group = new Group();
>>>>>>>   *     Task<Void> task = new Task<Void>() {
>>>>>>>   *         @@Override protected Void call() throws Exception {
>>>>>>>   *             for (int i=0; i<100; i++) {
>>>>>>>   *                 if (isCancelled()) break;
>>>>>>>   *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>   *                 r.setX(10 * i);
>>>>>>>   *                 Platform.runLater(new Runnable() {
>>>>>>>   *                     @@Override public void run() {
>>>>>>>   *                         group.getChildren().add(r);
>>>>>>>   *                     }
>>>>>>>   *                 });
>>>>>>>   *             }
>>>>>>>   *             return null;
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> * >>>>>>> *

Reacting To State Changes Generically

>>>>>>> * >>>>>>> *

Sometimes you may want to write a Task which updates its progress, >>>>>>> * message, text, or in some other way reacts whenever a state change >>>>>>> * happens on the Task. For example, you may want to change the status >>>>>>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>>>>>> *

>>>>>>> *

>>>>>>>   *     Task<Integer> task = new Task<Integer>() {
>>>>>>>   *         @@Override protected Integer call() throws Exception {
>>>>>>>   *             int iterations = 0;
>>>>>>>   *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>   *                 if (isCancelled()) {
>>>>>>>   *                     break;
>>>>>>>   *                 }
>>>>>>>   *                 System.out.println("Iteration " + iterations);
>>>>>>>   *             }
>>>>>>>   *             return iterations;
>>>>>>>   *         }
>>>>>>>   *
>>>>>>>   *         @@Override protected void succeeded() {
>>>>>>>   *             super.succeeded();
>>>>>>>   *             updateMessage("Done!");
>>>>>>>   *         }
>>>>>>>   *
>>>>>>>   *         @@Override protected void cancelled() {
>>>>>>>   *             super.cancelled();
>>>>>>>   *             updateMessage("Cancelled!");
>>>>>>>   *         }
>>>>>>>   *
>>>>>>>   *         @@Override protected void failed() {
>>>>>>>   *             super.failed();
>>>>>>>   *             updateMessage("Failed!");
>>>>>>>   *         }
>>>>>>>   *     }
>>>>>>>   *
>>>>>>> >>>>>>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>>>>>> >>>>>>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>>>>>> >>>>>>> Thanks everybody for your very valuable input! >>>>>>> Richard >>>>>>> >>>>>>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>>>>>> >>>>>>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>>>>>> >>>>>>>> Steve >>>>>>>> >>>>>>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>>>>>> Hi Richard, >>>>>>>>> >>>>>>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>>>>>> faulty code. >>>>>>>>> >>>>>>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>>>>>> especially the FutureTask class. I.e. provide an API to check >>>>>>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>>>>>> interrupt a thread: >>>>>>>>> >>>>>>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>>>>>> >>>>>>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>>>>>> of shitty code being written (surprise!), especially when threads are >>>>>>>>> involved. >>>>>>>>> >>>>>>>>> Cheers, Roman >>>>>>>>> >>>>>>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>>>>>> >>>>>>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>>>>>> >>>>>>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>>>>>> >>>>>>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>>>>>> >>>>>>>>>> If you write a Task, and do nothing smart: >>>>>>>>>> >>>>>>>>>> new Task() { >>>>>>>>>> public Object call() throws Exception { >>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>> doSomething(); >>>>>>>>>> } >>>>>>>>>> return null; >>>>>>>>>> } >>>>>>>>>> }; >>>>>>>>>> >>>>>>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>>>>>> >>>>>>>>>> new Task() { >>>>>>>>>> public Object call() throws Exception { >>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>> doSomething(); >>>>>>>>>> updateProgress(i, 100); >>>>>>>>>> } >>>>>>>>>> return null; >>>>>>>>>> } >>>>>>>>>> }; >>>>>>>>>> >>>>>>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>>>>>> >>>>>>>>>> In this case...: >>>>>>>>>> >>>>>>>>>> new Task() { >>>>>>>>>> public Object call() throws Exception { >>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>> try { >>>>>>>>>> doSomething(); >>>>>>>>>> updateProgress(i, 100); >>>>>>>>>> } catch (Exception e) { } >>>>>>>>>> } >>>>>>>>>> return null; >>>>>>>>>> } >>>>>>>>>> }; >>>>>>>>>> >>>>>>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>>>>>> >>>>>>>>>> new Task() { >>>>>>>>>> public Object call() throws Exception { >>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>> if (isCancelled()) break; >>>>>>>>>> try { >>>>>>>>>> doSomething(); >>>>>>>>>> updateProgress(i, 100); >>>>>>>>>> } catch (Exception e) { } >>>>>>>>>> } >>>>>>>>>> return null; >>>>>>>>>> } >>>>>>>>>> }; >>>>>>>>>> >>>>>>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>>>>>> >>>>>>>>>> new Task() { >>>>>>>>>> public Object call() throws Exception { >>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>> if (isCancelled()) { >>>>>>>>>> Platform.runLater(new Runnable() { >>>>>>>>>> public void run() { >>>>>>>>>> updateMessage("Cancelled") >>>>>>>>>> } >>>>>>>>>> }); >>>>>>>>>> break; >>>>>>>>>> } >>>>>>>>>> try { >>>>>>>>>> doSomething(); >>>>>>>>>> updateProgress(i, 100); >>>>>>>>>> } catch (Exception e) { } >>>>>>>>>> } >>>>>>>>>> return null; >>>>>>>>>> } >>>>>>>>>> }; >>>>>>>>>> >>>>>>>>>> new Task() { >>>>>>>>>> public Object call() throws Exception { >>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>> if (isCancelled()) break; >>>>>>>>>> try { >>>>>>>>>> doSomething(); >>>>>>>>>> updateProgress(i, 100); >>>>>>>>>> } catch (Exception e) { } >>>>>>>>>> } >>>>>>>>>> return null; >>>>>>>>>> } >>>>>>>>>> >>>>>>>>>> @Override protected void cancelled() { >>>>>>>>>> updateMessage("Cancelled"); >>>>>>>>>> } >>>>>>>>>> }; >>>>>>>>>> >>>>>>>>>> I think this works fairly well. Anything I'm missing? >>>>>>>>>> >>>>>>>>>> Thanks! >>>>>>>>>> Richard From jeff at reportmill.com Wed Jan 4 11:39:12 2012 From: jeff at reportmill.com (Jeff Martin) Date: Wed, 4 Jan 2012 13:39:12 -0600 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F04A758.8090404@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> Message-ID: <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> Another idea might be to throw the InterruptedException from a method called fireInterruptedException(), so savy rollback Tasks could override it to do nothing. jeff On Jan 4, 2012, at 1:24 PM, steve.x.northover at oracle.com wrote: > One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. > > Steve > > On 04/01/2012 2:11 PM, Richard Bair wrote: >> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >> >> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >> >> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >> >> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >> >> Richard >> >> >> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >> >>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>> >>> Steve From Richard.Bair at oracle.com Wed Jan 4 11:40:41 2012 From: Richard.Bair at oracle.com (Richard Bair) Date: Wed, 4 Jan 2012 11:40:41 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F04AA7F.1050105@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <4F04AA7F.1050105@oracle.com> Message-ID: <33607435-87C5-48FB-B22C-FDC861353E0C@oracle.com> Right, I wondered about that as well, though in the context of updateProgress(i, 1000, boolean???) or some other way to say "when I invoke this one, I mean it to allow updates after cancel, or not, or whatnot". On Jan 4, 2012, at 11:37 AM, steve.x.northover at oracle.com wrote: > > for (iterations=0; iterations<1000; iterations++) } > //ugly API but means that you can't update progress without checking for cancel or setting the message > if (isCancelled("Interation " + i, i) { > break; > } > ... do work ... > } > > > Steve > > On 04/01/2012 2:30 PM, Richard Bair wrote: >> What do you mean? >> >> On Jan 4, 2012, at 11:24 AM, steve.x.northover at oracle.com wrote: >> >>> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >>> >>> Steve >>> >>> On 04/01/2012 2:11 PM, Richard Bair wrote: >>>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>>> >>>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>>> >>>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>>> >>>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>>> >>>> Richard >>>> >>>> >>>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>>> >>>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>>> >>>>> Steve >>>>> >>>>> On 04/01/2012 2:00 PM, Richard Bair wrote: >>>>>> Today, they just work (they update the progress, title, message). >>>>>> >>>>>> Richard >>>>>> >>>>>> On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: >>>>>> >>>>>>> What happens now when updateProgress() and friends are called for a canceled task? >>>>>>> >>>>>>> Steve >>>>>>> >>>>>>> On 04/01/2012 1:55 PM, Richard Bair wrote: >>>>>>>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>>>>>>> >>>>>>>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>>>>>>> >>>>>>>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>>>>>>> >>>>>>>> *

Examples

>>>>>>>> *

>>>>>>>> * The following set of examples demonstrate some of the most common uses of >>>>>>>> * Tasks. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

A Simple Loop

>>>>>>>> * >>>>>>>> *

>>>>>>>> * The first example is a simple loop that does nothing particularly useful, >>>>>>>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>>>>>>> * example will simply loop and print to standard out on each loop iteration. >>>>>>>> * When it completes, it returns the number of times it iterated. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>>>  *             int iterations = 0;
>>>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>  *                 if (isCancelled()) {
>>>>>>>>  *                     break;
>>>>>>>>  *                 }
>>>>>>>>  *                 System.out.println("Iteration " + iterations);
>>>>>>>>  *             }
>>>>>>>>  *             return iterations;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

>>>>>>>> * First, we define what type of value is returned from this Task. In this >>>>>>>> * case, we want to return the number of times we iterated, so we will >>>>>>>> * specify the Task to be of type Integer by using generics. Then, within >>>>>>>> * the implementation of thecall method, we iterate from >>>>>>>> * 0 to 1000. On each iteration, we check to see whether this Task has >>>>>>>> * been cancelled. If it has been, then we break out of the loop and return >>>>>>>> * the number of times we iterated. Otherwise a message is printed to >>>>>>>> * the console and the iteration count increased and we continue looping. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>>>>>>> * developer may cancel the task, but the task will continue running >>>>>>>> * and updating both the progress and returning the incorrect result >>>>>>>> * from the end of thecall method. A correct implementation >>>>>>>> * of a Task will always check for cancellation. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

A Simple Loop With Progress Notification

>>>>>>>> * >>>>>>>> *

>>>>>>>> * Similar to the previous example, except this time we will modify the >>>>>>>> * progress of the Task in each iteration. Note that we have a choice >>>>>>>> * to make in the case of cancellation. Do we want to set the progress back >>>>>>>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>>>>>>> * the progress where it was at? In this case, lets leave the progress alone >>>>>>>> * and only update the message on cancellation, though updating the >>>>>>>> * progress after cancellation is a perfectly valid choice. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>>>  *             int iterations = 0;
>>>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>  *                 if (isCancelled()) {
>>>>>>>>  *                     updateMessage("Cancelled");
>>>>>>>>  *                     break;
>>>>>>>>  *                 }
>>>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>>>  *                 (iterations, 1000);
>>>>>>>>  *             }
>>>>>>>>  *             return iterations;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

>>>>>>>> * As before, within the for loop we check whether the Task has been >>>>>>>> * cancelled. If it has been cancelled, we will update the Task's >>>>>>>> * message to indicate that it has been cancelled, and then break as >>>>>>>> * before. If the Task has not been cancelled, then we will update its >>>>>>>> * message to indicate the current iteration and then update the >>>>>>>> * progress to indicate the current progress. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

A Simple Loop With Progress Notification And Blocking Calls

>>>>>>>> * >>>>>>>> *

>>>>>>>> * This example adds to the previous examples a blocking call. Because a >>>>>>>> * blocking call may thrown an InterruptedException, and because an >>>>>>>> * InterruptedException may occur as a result of the Task being cancelled, >>>>>>>> * we need to be sure to handle the InterruptedException and check on the >>>>>>>> * cancel state. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>>>  *             int iterations = 0;
>>>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>  *                 if (isCancelled()) {
>>>>>>>>  *                     updateMessage("Cancelled");
>>>>>>>>  *                     break;
>>>>>>>>  *                 }
>>>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>>>  *                 updateProgress(iterations, 1000);
>>>>>>>>  *
>>>>>>>>  *                 // Now block the thread for a short time, but be sure
>>>>>>>>  *                 // to check the interrupted exception for cancellation!
>>>>>>>>  *                 try {
>>>>>>>>  *                     Thread.sleep(100);
>>>>>>>>  *                 } catch (InterruptedException interrupted) {
>>>>>>>>  *                     if (isCancelled()) {
>>>>>>>>  *                         updateMessage("Cancelled");
>>>>>>>>  *                         break;
>>>>>>>>  *                     }
>>>>>>>>  *                 }
>>>>>>>>  *             }
>>>>>>>>  *             return iterations;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

>>>>>>>> * Here we have added to the body of the loop aThread.sleep >>>>>>>> * call. Since this is a blocking call, I have to handle the potential >>>>>>>> * InterruptedException. Within the catch block, I will check whether >>>>>>>> * the Task has been cancelled, and if so, update the message accordingly >>>>>>>> * and break out of the loop. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

A Task Which Takes Parameters

>>>>>>>> * >>>>>>>> *

>>>>>>>> * Most Tasks require some parameters in order to do useful work. For >>>>>>>> * example, a DeleteRecordTask needs the object or primary key to delete >>>>>>>> * from the database. A ReadFileTask needs the URI of the file to be read. >>>>>>>> * Because Tasks operate on a background thread, care must be taken to >>>>>>>> * make sure the body of thecall method does not read or >>>>>>>> * modify any shared state. There are two techniques most useful for >>>>>>>> * doing this: using final variables, and passing variables to a Task >>>>>>>> * during construction. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>> * When using a Task as an anonymous class, the most natural way to pass >>>>>>>> * parameters to the Task is by using final variables. In this example, >>>>>>>> * we pass to the Task the total number of times the Task should iterate. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     final int totalIterations = 900;
>>>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>>>  *             int iterations = 0;
>>>>>>>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>>  *                 if (isCancelled()) {
>>>>>>>>  *                     updateMessage("Cancelled");
>>>>>>>>  *                     break;
>>>>>>>>  *                 }
>>>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>>>  *                 updateProgress(iterations, totalIterations);
>>>>>>>>  *             }
>>>>>>>>  *             return iterations;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

>>>>>>>> * SincetotalIterations is final, thecall >>>>>>>> * method can safely read it and refer to it from a background thread. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>> * When writing Task libraries (as opposed to specific-use implementations), >>>>>>>> * we need to use a different technique. In this case, I will create an >>>>>>>> * IteratingTask which performs the same work as above. This time, since >>>>>>>> * the IteratingTask is defined in its own file, it will need to have >>>>>>>> * parameters passed to it in its constructor. These parameters are >>>>>>>> * assigned to final variables. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     public class IteratingTask extends Task<Integer> {
>>>>>>>>  *         private final int totalIterations;
>>>>>>>>  *
>>>>>>>>  *         public IteratingTask(int totalIterations) {
>>>>>>>>  *             this.totalIterations = totalIterations;
>>>>>>>>  *         }
>>>>>>>>  *
>>>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>>>  *             int iterations = 0;
>>>>>>>>  *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>>  *                 if (isCancelled()) {
>>>>>>>>  *                     updateMessage("Cancelled");
>>>>>>>>  *                     break;
>>>>>>>>  *                 }
>>>>>>>>  *                 updateMessage("Iteration " + iterations);
>>>>>>>>  *                 updateProgress(iterations, totalIterations);
>>>>>>>>  *             }
>>>>>>>>  *             return iterations;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

And then when used:

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     IteratingTask task = new IteratingTask(800);
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

In this way, parameters are passed to the IteratingTask in a safe >>>>>>>> * manner, and again, are final. Thus, thecall method can >>>>>>>> * safely read this state from a background thread.

>>>>>>>> * >>>>>>>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>>>>>>> * from a background thread. Doing so may introduce race conditions. In >>>>>>>> * particular, suppose you had a SaveCustomerTask which took a Customer >>>>>>>> * in its constructor. Although the SaveCustomerTask may have a final >>>>>>>> * reference to the Customer, if the Customer object is mutable, then it >>>>>>>> * is possible that both the SaveCustomerTask and some other application code >>>>>>>> * will be reading or modifying the state of the Customer from different >>>>>>>> * threads. Be very careful in such cases, that while a mutable object such >>>>>>>> * as this Customer is being used from a background thread, that it is >>>>>>>> * not being used also from another thread. In particular, if the background >>>>>>>> * thread is reading data from the database and updating the Customer object, >>>>>>>> * and the Customer object is bound to scene graph nodes (such as UI >>>>>>>> * controls), then there could be a violation of threading rules! For such >>>>>>>> * cases, modify the Customer object from the FX Application Thread rather >>>>>>>> * than from the background thread.

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     public class UpdateCustomerTask extends Task<Customer> {
>>>>>>>>  *         private final Customer customer;
>>>>>>>>  *
>>>>>>>>  *         public UpdateCustomerTask(Customer customer) {
>>>>>>>>  *             this.customer = customer;
>>>>>>>>  *         }
>>>>>>>>  *
>>>>>>>>  *         @@Override protected Customer call() throws Exception {
>>>>>>>>  *             // pseudo-code:
>>>>>>>>  *             //   query the database
>>>>>>>>  *             //   read the values
>>>>>>>>  *
>>>>>>>>  *             // Now update the customer
>>>>>>>>  *             Platform.runLater(new Runnable() {
>>>>>>>>  *                 @@Override public void run() {
>>>>>>>>  *                     customer.firstName(rs.getString("FirstName"));
>>>>>>>>  *                     // etc
>>>>>>>>  *                 }
>>>>>>>>  *             });
>>>>>>>>  *
>>>>>>>>  *             return customer;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

A Task Which Returns No Value

>>>>>>>> * >>>>>>>> *

>>>>>>>> * Many, if not most, Tasks should return a value upon completion. For >>>>>>>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>>>>>>> * created object or primary key, a "Read" Task would return the read >>>>>>>> * object, an "Update" task would return the number of records updated, >>>>>>>> * and a "Delete" task would return the number of records deleted. >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>> * However sometimes there just isn't anything truly useful to return. >>>>>>>> * For example, I might have a Task which writes to a file. Task has built >>>>>>>> * into it a mechanism for indicating whether it has succeeded or failed >>>>>>>> * along with the number of bytes written (the progress), and thus there is >>>>>>>> * nothing really for me to return. In such a case, you can use the Void >>>>>>>> * type. This is a special type in the Java language which can only be >>>>>>>> * assigned the value ofnull. You would use it as follows: >>>>>>>> *

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     final String filePath = "/foo.txt";
>>>>>>>>  *     final String contents = "Some contents";
>>>>>>>>  *     Task<Void> task = new Task<Void>() {
>>>>>>>>  *         @@Override protected Void call() throws Exception {
>>>>>>>>  *             File file = new File(filePath);
>>>>>>>>  *             FileOutputStream out = new FileOutputStream(file);
>>>>>>>>  *             // ... and other code to write the contents ...
>>>>>>>>  *
>>>>>>>>  *             // Return null at the end of a Task of type Void
>>>>>>>>  *             return null;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

A Task Which Returns An ObservableList

>>>>>>>> * >>>>>>>> *

Because the ListView, TableView, and other UI controls and scene graph >>>>>>>> * nodes make use of ObservableList, it is common to want to create and return >>>>>>>> * an ObservableList from a Task. When you do not care to display intermediate >>>>>>>> * values, the easiest way to correctly write such a Task is simply to >>>>>>>> * construct an ObservableList within thecall method, and then >>>>>>>> * return it at the conclusion of the Task.

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>>>>>>  *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>>>>>>  *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>>>>>>  *             for (int i=0; i<100; i++) {
>>>>>>>>  *                 if (isCancelled()) break;
>>>>>>>>  *                 Rectangle r = new Rectangle(10, 10);
>>>>>>>>  *                 r.setX(10 * i);
>>>>>>>>  *                 results.add(r);
>>>>>>>>  *             }
>>>>>>>>  *             return results;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

In the above example, we are going to create 100 rectangles and return >>>>>>>> * them from this task. An ObservableList is created within the >>>>>>>> *call method, populated, and then returned.

>>>>>>>> * >>>>>>>> *

A Task Which Returns Partial Results

>>>>>>>> * >>>>>>>> *

Sometimes you want to create a Task which will return partial results. >>>>>>>> * Perhaps you are building a complex scene graph and want to show the >>>>>>>> * scene graph as it is being constructed. Or perhaps you are reading a large >>>>>>>> * amount of data over the network and want to display the entries in a >>>>>>>> * TableView as the data is arriving. In such cases, there is some shared state >>>>>>>> * available both to the FX Application Thread and the background thread. >>>>>>>> * Great care must be taken tonever update shared state from any >>>>>>>> * thread other than the FX Application Thread.

>>>>>>>> * >>>>>>>> *

The easiest way to do this is to expose a new property on the Task >>>>>>>> * which will represent the partial result. Then make sure to use >>>>>>>> *Platform.runLater when adding new items to the partial >>>>>>>> * result.

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>>>>>>  *         // Uses Java 7 diamond operator
>>>>>>>>  *         private ReadOnlyObjectWrapper>     partialResults =
>>>>>>>>  *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>>>>>>  *
>>>>>>>>  *         public final ObservableList     getPartialResults() { return partialResults; }
>>>>>>>>  *         public final ReadOnlyObjectProperty>     partialResultsProperty() {
>>>>>>>>  *             return partialResults.getReadOnlyProperty();
>>>>>>>>  *         }
>>>>>>>>  *
>>>>>>>>  *         @@Override protected ObservableList     call() throws Exception {
>>>>>>>>  *             for (int i=0; i<100; i++) {
>>>>>>>>  *                 if (isCancelled()) break;
>>>>>>>>  *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>>  *                 r.setX(10 * i);
>>>>>>>>  *                 Platform.runLater(new Runnable() {
>>>>>>>>  *                     @@Override public void run() {
>>>>>>>>  *                         partialResults.add(r);
>>>>>>>>  *                     }
>>>>>>>>  *                 });
>>>>>>>>  *             }
>>>>>>>>  *             return partialResults;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> * >>>>>>>> * >>>>>>>> *

A Task Which Modifies The Scene Graph

>>>>>>>> * >>>>>>>> *

Generally, Tasks should not interact directly with the UI. Doing so >>>>>>>> * creates a tight coupling between a specific Task implementation and a >>>>>>>> * specific part of your UI. However, when you do want to create such a >>>>>>>> * coupling, you must ensure that you usePlatform.runLater >>>>>>>> * so that any modifications of the scene graph occur on the >>>>>>>> * FX Application Thread.

>>>>>>>> * >>>>>>>> *

>>>>>>>>  *     final Group group = new Group();
>>>>>>>>  *     Task<Void> task = new Task<Void>() {
>>>>>>>>  *         @@Override protected Void call() throws Exception {
>>>>>>>>  *             for (int i=0; i<100; i++) {
>>>>>>>>  *                 if (isCancelled()) break;
>>>>>>>>  *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>>  *                 r.setX(10 * i);
>>>>>>>>  *                 Platform.runLater(new Runnable() {
>>>>>>>>  *                     @@Override public void run() {
>>>>>>>>  *                         group.getChildren().add(r);
>>>>>>>>  *                     }
>>>>>>>>  *                 });
>>>>>>>>  *             }
>>>>>>>>  *             return null;
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> * >>>>>>>> *

Reacting To State Changes Generically

>>>>>>>> * >>>>>>>> *

Sometimes you may want to write a Task which updates its progress, >>>>>>>> * message, text, or in some other way reacts whenever a state change >>>>>>>> * happens on the Task. For example, you may want to change the status >>>>>>>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>>>>>>> *

>>>>>>>> *

>>>>>>>>  *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>  *         @@Override protected Integer call() throws Exception {
>>>>>>>>  *             int iterations = 0;
>>>>>>>>  *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>  *                 if (isCancelled()) {
>>>>>>>>  *                     break;
>>>>>>>>  *                 }
>>>>>>>>  *                 System.out.println("Iteration " + iterations);
>>>>>>>>  *             }
>>>>>>>>  *             return iterations;
>>>>>>>>  *         }
>>>>>>>>  *
>>>>>>>>  *         @@Override protected void succeeded() {
>>>>>>>>  *             super.succeeded();
>>>>>>>>  *             updateMessage("Done!");
>>>>>>>>  *         }
>>>>>>>>  *
>>>>>>>>  *         @@Override protected void cancelled() {
>>>>>>>>  *             super.cancelled();
>>>>>>>>  *             updateMessage("Cancelled!");
>>>>>>>>  *         }
>>>>>>>>  *
>>>>>>>>  *         @@Override protected void failed() {
>>>>>>>>  *             super.failed();
>>>>>>>>  *             updateMessage("Failed!");
>>>>>>>>  *         }
>>>>>>>>  *     }
>>>>>>>>  *
>>>>>>>> >>>>>>>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>>>>>>> >>>>>>>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>>>>>>> >>>>>>>> Thanks everybody for your very valuable input! >>>>>>>> Richard >>>>>>>> >>>>>>>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>>>>>>> >>>>>>>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>>>>>>> >>>>>>>>> Steve >>>>>>>>> >>>>>>>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>>>>>>> Hi Richard, >>>>>>>>>> >>>>>>>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>>>>>>> faulty code. >>>>>>>>>> >>>>>>>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>>>>>>> especially the FutureTask class. I.e. provide an API to check >>>>>>>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>>>>>>> interrupt a thread: >>>>>>>>>> >>>>>>>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>>>>>>> >>>>>>>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>>>>>>> of shitty code being written (surprise!), especially when threads are >>>>>>>>>> involved. >>>>>>>>>> >>>>>>>>>> Cheers, Roman >>>>>>>>>> >>>>>>>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>>>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>>>>>>> >>>>>>>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>>>>>>> >>>>>>>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>>>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>>>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>>>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>>>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>>>>>>> >>>>>>>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>>>>>>> >>>>>>>>>>> If you write a Task, and do nothing smart: >>>>>>>>>>> >>>>>>>>>>> new Task() { >>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>> doSomething(); >>>>>>>>>>> } >>>>>>>>>>> return null; >>>>>>>>>>> } >>>>>>>>>>> }; >>>>>>>>>>> >>>>>>>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>>>>>>> >>>>>>>>>>> new Task() { >>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>> doSomething(); >>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>> } >>>>>>>>>>> return null; >>>>>>>>>>> } >>>>>>>>>>> }; >>>>>>>>>>> >>>>>>>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>>>>>>> >>>>>>>>>>> In this case...: >>>>>>>>>>> >>>>>>>>>>> new Task() { >>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>> try { >>>>>>>>>>> doSomething(); >>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>> } >>>>>>>>>>> return null; >>>>>>>>>>> } >>>>>>>>>>> }; >>>>>>>>>>> >>>>>>>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>>>>>>> >>>>>>>>>>> new Task() { >>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>> if (isCancelled()) break; >>>>>>>>>>> try { >>>>>>>>>>> doSomething(); >>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>> } >>>>>>>>>>> return null; >>>>>>>>>>> } >>>>>>>>>>> }; >>>>>>>>>>> >>>>>>>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>>>>>>> >>>>>>>>>>> new Task() { >>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>> if (isCancelled()) { >>>>>>>>>>> Platform.runLater(new Runnable() { >>>>>>>>>>> public void run() { >>>>>>>>>>> updateMessage("Cancelled") >>>>>>>>>>> } >>>>>>>>>>> }); >>>>>>>>>>> break; >>>>>>>>>>> } >>>>>>>>>>> try { >>>>>>>>>>> doSomething(); >>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>> } >>>>>>>>>>> return null; >>>>>>>>>>> } >>>>>>>>>>> }; >>>>>>>>>>> >>>>>>>>>>> new Task() { >>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>> if (isCancelled()) break; >>>>>>>>>>> try { >>>>>>>>>>> doSomething(); >>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>> } >>>>>>>>>>> return null; >>>>>>>>>>> } >>>>>>>>>>> >>>>>>>>>>> @Override protected void cancelled() { >>>>>>>>>>> updateMessage("Cancelled"); >>>>>>>>>>> } >>>>>>>>>>> }; >>>>>>>>>>> >>>>>>>>>>> I think this works fairly well. Anything I'm missing? >>>>>>>>>>> >>>>>>>>>>> Thanks! >>>>>>>>>>> Richard From richard.bair at oracle.com Wed Jan 4 11:42:40 2012 From: richard.bair at oracle.com (Richard Bair) Date: Wed, 4 Jan 2012 11:42:40 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <33607435-87C5-48FB-B22C-FDC861353E0C@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <4F04AA7F.1050105@oracle.com> <33607435-87C5-48FB-B22C-FDC861353E0C@oracle.com> Message-ID: <1ADF64D6-BB8A-4E47-AA66-8DF2AC7CD67F@oracle.com> Though once I went down this road, I thought "well, if it is opt-in, then it really isn't buying me much beyond just a runLater which we already have, and if it is opt-out it already breaks people", so I didn't pursue this farther. On Jan 4, 2012, at 11:40 AM, Richard Bair wrote: > Right, I wondered about that as well, though in the context of updateProgress(i, 1000, boolean???) or some other way to say "when I invoke this one, I mean it to allow updates after cancel, or not, or whatnot". > > > On Jan 4, 2012, at 11:37 AM, steve.x.northover at oracle.com wrote: > >> >> for (iterations=0; iterations<1000; iterations++) } >> //ugly API but means that you can't update progress without checking for cancel or setting the message >> if (isCancelled("Interation " + i, i) { >> break; >> } >> ... do work ... >> } >> >> >> Steve >> >> On 04/01/2012 2:30 PM, Richard Bair wrote: >>> What do you mean? >>> >>> On Jan 4, 2012, at 11:24 AM, steve.x.northover at oracle.com wrote: >>> >>>> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >>>> >>>> Steve >>>> >>>> On 04/01/2012 2:11 PM, Richard Bair wrote: >>>>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>>>> >>>>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>>>> >>>>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>>>> >>>>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>>>> >>>>> Richard >>>>> >>>>> >>>>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>>>> >>>>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>>>> >>>>>> Steve >>>>>> >>>>>> On 04/01/2012 2:00 PM, Richard Bair wrote: >>>>>>> Today, they just work (they update the progress, title, message). >>>>>>> >>>>>>> Richard >>>>>>> >>>>>>> On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: >>>>>>> >>>>>>>> What happens now when updateProgress() and friends are called for a canceled task? >>>>>>>> >>>>>>>> Steve >>>>>>>> >>>>>>>> On 04/01/2012 1:55 PM, Richard Bair wrote: >>>>>>>>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>>>>>>>> >>>>>>>>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>>>>>>>> >>>>>>>>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>>>>>>>> >>>>>>>>> *

Examples

>>>>>>>>> *

>>>>>>>>> * The following set of examples demonstrate some of the most common uses of >>>>>>>>> * Tasks. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

A Simple Loop

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * The first example is a simple loop that does nothing particularly useful, >>>>>>>>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>>>>>>>> * example will simply loop and print to standard out on each loop iteration. >>>>>>>>> * When it completes, it returns the number of times it iterated. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>> *             int iterations = 0;
>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>> *                     break;
>>>>>>>>> *                 }
>>>>>>>>> *                 System.out.println("Iteration " + iterations);
>>>>>>>>> *             }
>>>>>>>>> *             return iterations;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * First, we define what type of value is returned from this Task. In this >>>>>>>>> * case, we want to return the number of times we iterated, so we will >>>>>>>>> * specify the Task to be of type Integer by using generics. Then, within >>>>>>>>> * the implementation of thecall method, we iterate from >>>>>>>>> * 0 to 1000. On each iteration, we check to see whether this Task has >>>>>>>>> * been cancelled. If it has been, then we break out of the loop and return >>>>>>>>> * the number of times we iterated. Otherwise a message is printed to >>>>>>>>> * the console and the iteration count increased and we continue looping. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>>>>>>>> * developer may cancel the task, but the task will continue running >>>>>>>>> * and updating both the progress and returning the incorrect result >>>>>>>>> * from the end of thecall method. A correct implementation >>>>>>>>> * of a Task will always check for cancellation. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

A Simple Loop With Progress Notification

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * Similar to the previous example, except this time we will modify the >>>>>>>>> * progress of the Task in each iteration. Note that we have a choice >>>>>>>>> * to make in the case of cancellation. Do we want to set the progress back >>>>>>>>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>>>>>>>> * the progress where it was at? In this case, lets leave the progress alone >>>>>>>>> * and only update the message on cancellation, though updating the >>>>>>>>> * progress after cancellation is a perfectly valid choice. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>> *             int iterations = 0;
>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>> *                     break;
>>>>>>>>> *                 }
>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>> *                 (iterations, 1000);
>>>>>>>>> *             }
>>>>>>>>> *             return iterations;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * As before, within the for loop we check whether the Task has been >>>>>>>>> * cancelled. If it has been cancelled, we will update the Task's >>>>>>>>> * message to indicate that it has been cancelled, and then break as >>>>>>>>> * before. If the Task has not been cancelled, then we will update its >>>>>>>>> * message to indicate the current iteration and then update the >>>>>>>>> * progress to indicate the current progress. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

A Simple Loop With Progress Notification And Blocking Calls

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * This example adds to the previous examples a blocking call. Because a >>>>>>>>> * blocking call may thrown an InterruptedException, and because an >>>>>>>>> * InterruptedException may occur as a result of the Task being cancelled, >>>>>>>>> * we need to be sure to handle the InterruptedException and check on the >>>>>>>>> * cancel state. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>> *             int iterations = 0;
>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>> *                     break;
>>>>>>>>> *                 }
>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>> *                 updateProgress(iterations, 1000);
>>>>>>>>> *
>>>>>>>>> *                 // Now block the thread for a short time, but be sure
>>>>>>>>> *                 // to check the interrupted exception for cancellation!
>>>>>>>>> *                 try {
>>>>>>>>> *                     Thread.sleep(100);
>>>>>>>>> *                 } catch (InterruptedException interrupted) {
>>>>>>>>> *                     if (isCancelled()) {
>>>>>>>>> *                         updateMessage("Cancelled");
>>>>>>>>> *                         break;
>>>>>>>>> *                     }
>>>>>>>>> *                 }
>>>>>>>>> *             }
>>>>>>>>> *             return iterations;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * Here we have added to the body of the loop aThread.sleep >>>>>>>>> * call. Since this is a blocking call, I have to handle the potential >>>>>>>>> * InterruptedException. Within the catch block, I will check whether >>>>>>>>> * the Task has been cancelled, and if so, update the message accordingly >>>>>>>>> * and break out of the loop. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

A Task Which Takes Parameters

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * Most Tasks require some parameters in order to do useful work. For >>>>>>>>> * example, a DeleteRecordTask needs the object or primary key to delete >>>>>>>>> * from the database. A ReadFileTask needs the URI of the file to be read. >>>>>>>>> * Because Tasks operate on a background thread, care must be taken to >>>>>>>>> * make sure the body of thecall method does not read or >>>>>>>>> * modify any shared state. There are two techniques most useful for >>>>>>>>> * doing this: using final variables, and passing variables to a Task >>>>>>>>> * during construction. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * When using a Task as an anonymous class, the most natural way to pass >>>>>>>>> * parameters to the Task is by using final variables. In this example, >>>>>>>>> * we pass to the Task the total number of times the Task should iterate. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     final int totalIterations = 900;
>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>> *             int iterations = 0;
>>>>>>>>> *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>> *                     break;
>>>>>>>>> *                 }
>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>> *                 updateProgress(iterations, totalIterations);
>>>>>>>>> *             }
>>>>>>>>> *             return iterations;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * SincetotalIterations is final, thecall >>>>>>>>> * method can safely read it and refer to it from a background thread. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * When writing Task libraries (as opposed to specific-use implementations), >>>>>>>>> * we need to use a different technique. In this case, I will create an >>>>>>>>> * IteratingTask which performs the same work as above. This time, since >>>>>>>>> * the IteratingTask is defined in its own file, it will need to have >>>>>>>>> * parameters passed to it in its constructor. These parameters are >>>>>>>>> * assigned to final variables. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     public class IteratingTask extends Task<Integer> {
>>>>>>>>> *         private final int totalIterations;
>>>>>>>>> *
>>>>>>>>> *         public IteratingTask(int totalIterations) {
>>>>>>>>> *             this.totalIterations = totalIterations;
>>>>>>>>> *         }
>>>>>>>>> *
>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>> *             int iterations = 0;
>>>>>>>>> *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>> *                     break;
>>>>>>>>> *                 }
>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>> *                 updateProgress(iterations, totalIterations);
>>>>>>>>> *             }
>>>>>>>>> *             return iterations;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

And then when used:

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     IteratingTask task = new IteratingTask(800);
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

In this way, parameters are passed to the IteratingTask in a safe >>>>>>>>> * manner, and again, are final. Thus, thecall method can >>>>>>>>> * safely read this state from a background thread.

>>>>>>>>> * >>>>>>>>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>>>>>>>> * from a background thread. Doing so may introduce race conditions. In >>>>>>>>> * particular, suppose you had a SaveCustomerTask which took a Customer >>>>>>>>> * in its constructor. Although the SaveCustomerTask may have a final >>>>>>>>> * reference to the Customer, if the Customer object is mutable, then it >>>>>>>>> * is possible that both the SaveCustomerTask and some other application code >>>>>>>>> * will be reading or modifying the state of the Customer from different >>>>>>>>> * threads. Be very careful in such cases, that while a mutable object such >>>>>>>>> * as this Customer is being used from a background thread, that it is >>>>>>>>> * not being used also from another thread. In particular, if the background >>>>>>>>> * thread is reading data from the database and updating the Customer object, >>>>>>>>> * and the Customer object is bound to scene graph nodes (such as UI >>>>>>>>> * controls), then there could be a violation of threading rules! For such >>>>>>>>> * cases, modify the Customer object from the FX Application Thread rather >>>>>>>>> * than from the background thread.

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     public class UpdateCustomerTask extends Task<Customer> {
>>>>>>>>> *         private final Customer customer;
>>>>>>>>> *
>>>>>>>>> *         public UpdateCustomerTask(Customer customer) {
>>>>>>>>> *             this.customer = customer;
>>>>>>>>> *         }
>>>>>>>>> *
>>>>>>>>> *         @@Override protected Customer call() throws Exception {
>>>>>>>>> *             // pseudo-code:
>>>>>>>>> *             //   query the database
>>>>>>>>> *             //   read the values
>>>>>>>>> *
>>>>>>>>> *             // Now update the customer
>>>>>>>>> *             Platform.runLater(new Runnable() {
>>>>>>>>> *                 @@Override public void run() {
>>>>>>>>> *                     customer.firstName(rs.getString("FirstName"));
>>>>>>>>> *                     // etc
>>>>>>>>> *                 }
>>>>>>>>> *             });
>>>>>>>>> *
>>>>>>>>> *             return customer;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

A Task Which Returns No Value

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * Many, if not most, Tasks should return a value upon completion. For >>>>>>>>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>>>>>>>> * created object or primary key, a "Read" Task would return the read >>>>>>>>> * object, an "Update" task would return the number of records updated, >>>>>>>>> * and a "Delete" task would return the number of records deleted. >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> * However sometimes there just isn't anything truly useful to return. >>>>>>>>> * For example, I might have a Task which writes to a file. Task has built >>>>>>>>> * into it a mechanism for indicating whether it has succeeded or failed >>>>>>>>> * along with the number of bytes written (the progress), and thus there is >>>>>>>>> * nothing really for me to return. In such a case, you can use the Void >>>>>>>>> * type. This is a special type in the Java language which can only be >>>>>>>>> * assigned the value ofnull. You would use it as follows: >>>>>>>>> *

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     final String filePath = "/foo.txt";
>>>>>>>>> *     final String contents = "Some contents";
>>>>>>>>> *     Task<Void> task = new Task<Void>() {
>>>>>>>>> *         @@Override protected Void call() throws Exception {
>>>>>>>>> *             File file = new File(filePath);
>>>>>>>>> *             FileOutputStream out = new FileOutputStream(file);
>>>>>>>>> *             // ... and other code to write the contents ...
>>>>>>>>> *
>>>>>>>>> *             // Return null at the end of a Task of type Void
>>>>>>>>> *             return null;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

A Task Which Returns An ObservableList

>>>>>>>>> * >>>>>>>>> *

Because the ListView, TableView, and other UI controls and scene graph >>>>>>>>> * nodes make use of ObservableList, it is common to want to create and return >>>>>>>>> * an ObservableList from a Task. When you do not care to display intermediate >>>>>>>>> * values, the easiest way to correctly write such a Task is simply to >>>>>>>>> * construct an ObservableList within thecall method, and then >>>>>>>>> * return it at the conclusion of the Task.

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>>>>>>> *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>>>>>>> *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>>>>>>> *             for (int i=0; i<100; i++) {
>>>>>>>>> *                 if (isCancelled()) break;
>>>>>>>>> *                 Rectangle r = new Rectangle(10, 10);
>>>>>>>>> *                 r.setX(10 * i);
>>>>>>>>> *                 results.add(r);
>>>>>>>>> *             }
>>>>>>>>> *             return results;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

In the above example, we are going to create 100 rectangles and return >>>>>>>>> * them from this task. An ObservableList is created within the >>>>>>>>> *call method, populated, and then returned.

>>>>>>>>> * >>>>>>>>> *

A Task Which Returns Partial Results

>>>>>>>>> * >>>>>>>>> *

Sometimes you want to create a Task which will return partial results. >>>>>>>>> * Perhaps you are building a complex scene graph and want to show the >>>>>>>>> * scene graph as it is being constructed. Or perhaps you are reading a large >>>>>>>>> * amount of data over the network and want to display the entries in a >>>>>>>>> * TableView as the data is arriving. In such cases, there is some shared state >>>>>>>>> * available both to the FX Application Thread and the background thread. >>>>>>>>> * Great care must be taken tonever update shared state from any >>>>>>>>> * thread other than the FX Application Thread.

>>>>>>>>> * >>>>>>>>> *

The easiest way to do this is to expose a new property on the Task >>>>>>>>> * which will represent the partial result. Then make sure to use >>>>>>>>> *Platform.runLater when adding new items to the partial >>>>>>>>> * result.

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>>>>>>> *         // Uses Java 7 diamond operator
>>>>>>>>> *         private ReadOnlyObjectWrapper>     partialResults =
>>>>>>>>> *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>>>>>>> *
>>>>>>>>> *         public final ObservableList     getPartialResults() { return partialResults; }
>>>>>>>>> *         public final ReadOnlyObjectProperty>     partialResultsProperty() {
>>>>>>>>> *             return partialResults.getReadOnlyProperty();
>>>>>>>>> *         }
>>>>>>>>> *
>>>>>>>>> *         @@Override protected ObservableList     call() throws Exception {
>>>>>>>>> *             for (int i=0; i<100; i++) {
>>>>>>>>> *                 if (isCancelled()) break;
>>>>>>>>> *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>>> *                 r.setX(10 * i);
>>>>>>>>> *                 Platform.runLater(new Runnable() {
>>>>>>>>> *                     @@Override public void run() {
>>>>>>>>> *                         partialResults.add(r);
>>>>>>>>> *                     }
>>>>>>>>> *                 });
>>>>>>>>> *             }
>>>>>>>>> *             return partialResults;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> * >>>>>>>>> * >>>>>>>>> *

A Task Which Modifies The Scene Graph

>>>>>>>>> * >>>>>>>>> *

Generally, Tasks should not interact directly with the UI. Doing so >>>>>>>>> * creates a tight coupling between a specific Task implementation and a >>>>>>>>> * specific part of your UI. However, when you do want to create such a >>>>>>>>> * coupling, you must ensure that you usePlatform.runLater >>>>>>>>> * so that any modifications of the scene graph occur on the >>>>>>>>> * FX Application Thread.

>>>>>>>>> * >>>>>>>>> *

>>>>>>>>> *     final Group group = new Group();
>>>>>>>>> *     Task<Void> task = new Task<Void>() {
>>>>>>>>> *         @@Override protected Void call() throws Exception {
>>>>>>>>> *             for (int i=0; i<100; i++) {
>>>>>>>>> *                 if (isCancelled()) break;
>>>>>>>>> *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>>> *                 r.setX(10 * i);
>>>>>>>>> *                 Platform.runLater(new Runnable() {
>>>>>>>>> *                     @@Override public void run() {
>>>>>>>>> *                         group.getChildren().add(r);
>>>>>>>>> *                     }
>>>>>>>>> *                 });
>>>>>>>>> *             }
>>>>>>>>> *             return null;
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> * >>>>>>>>> *

Reacting To State Changes Generically

>>>>>>>>> * >>>>>>>>> *

Sometimes you may want to write a Task which updates its progress, >>>>>>>>> * message, text, or in some other way reacts whenever a state change >>>>>>>>> * happens on the Task. For example, you may want to change the status >>>>>>>>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>>>>>>>> *

>>>>>>>>> *

>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>> *             int iterations = 0;
>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>> *                     break;
>>>>>>>>> *                 }
>>>>>>>>> *                 System.out.println("Iteration " + iterations);
>>>>>>>>> *             }
>>>>>>>>> *             return iterations;
>>>>>>>>> *         }
>>>>>>>>> *
>>>>>>>>> *         @@Override protected void succeeded() {
>>>>>>>>> *             super.succeeded();
>>>>>>>>> *             updateMessage("Done!");
>>>>>>>>> *         }
>>>>>>>>> *
>>>>>>>>> *         @@Override protected void cancelled() {
>>>>>>>>> *             super.cancelled();
>>>>>>>>> *             updateMessage("Cancelled!");
>>>>>>>>> *         }
>>>>>>>>> *
>>>>>>>>> *         @@Override protected void failed() {
>>>>>>>>> *             super.failed();
>>>>>>>>> *             updateMessage("Failed!");
>>>>>>>>> *         }
>>>>>>>>> *     }
>>>>>>>>> *
>>>>>>>>> >>>>>>>>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>>>>>>>> >>>>>>>>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>>>>>>>> >>>>>>>>> Thanks everybody for your very valuable input! >>>>>>>>> Richard >>>>>>>>> >>>>>>>>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>>>>>>>> >>>>>>>>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>>>>>>>> >>>>>>>>>> Steve >>>>>>>>>> >>>>>>>>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>>>>>>>> Hi Richard, >>>>>>>>>>> >>>>>>>>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>>>>>>>> faulty code. >>>>>>>>>>> >>>>>>>>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>>>>>>>> especially the FutureTask class. I.e. provide an API to check >>>>>>>>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>>>>>>>> interrupt a thread: >>>>>>>>>>> >>>>>>>>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>>>>>>>> >>>>>>>>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>>>>>>>> of shitty code being written (surprise!), especially when threads are >>>>>>>>>>> involved. >>>>>>>>>>> >>>>>>>>>>> Cheers, Roman >>>>>>>>>>> >>>>>>>>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>>>>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>>>>>>>> >>>>>>>>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>>>>>>>> >>>>>>>>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>>>>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>>>>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>>>>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>>>>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>>>>>>>> >>>>>>>>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>>>>>>>> >>>>>>>>>>>> If you write a Task, and do nothing smart: >>>>>>>>>>>> >>>>>>>>>>>> new Task() { >>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>> doSomething(); >>>>>>>>>>>> } >>>>>>>>>>>> return null; >>>>>>>>>>>> } >>>>>>>>>>>> }; >>>>>>>>>>>> >>>>>>>>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>>>>>>>> >>>>>>>>>>>> new Task() { >>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>> doSomething(); >>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>> } >>>>>>>>>>>> return null; >>>>>>>>>>>> } >>>>>>>>>>>> }; >>>>>>>>>>>> >>>>>>>>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>>>>>>>> >>>>>>>>>>>> In this case...: >>>>>>>>>>>> >>>>>>>>>>>> new Task() { >>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>> try { >>>>>>>>>>>> doSomething(); >>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>> } >>>>>>>>>>>> return null; >>>>>>>>>>>> } >>>>>>>>>>>> }; >>>>>>>>>>>> >>>>>>>>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>>>>>>>> >>>>>>>>>>>> new Task() { >>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>> if (isCancelled()) break; >>>>>>>>>>>> try { >>>>>>>>>>>> doSomething(); >>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>> } >>>>>>>>>>>> return null; >>>>>>>>>>>> } >>>>>>>>>>>> }; >>>>>>>>>>>> >>>>>>>>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>>>>>>>> >>>>>>>>>>>> new Task() { >>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>> if (isCancelled()) { >>>>>>>>>>>> Platform.runLater(new Runnable() { >>>>>>>>>>>> public void run() { >>>>>>>>>>>> updateMessage("Cancelled") >>>>>>>>>>>> } >>>>>>>>>>>> }); >>>>>>>>>>>> break; >>>>>>>>>>>> } >>>>>>>>>>>> try { >>>>>>>>>>>> doSomething(); >>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>> } >>>>>>>>>>>> return null; >>>>>>>>>>>> } >>>>>>>>>>>> }; >>>>>>>>>>>> >>>>>>>>>>>> new Task() { >>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>> if (isCancelled()) break; >>>>>>>>>>>> try { >>>>>>>>>>>> doSomething(); >>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>> } >>>>>>>>>>>> return null; >>>>>>>>>>>> } >>>>>>>>>>>> >>>>>>>>>>>> @Override protected void cancelled() { >>>>>>>>>>>> updateMessage("Cancelled"); >>>>>>>>>>>> } >>>>>>>>>>>> }; >>>>>>>>>>>> >>>>>>>>>>>> I think this works fairly well. Anything I'm missing? >>>>>>>>>>>> >>>>>>>>>>>> Thanks! >>>>>>>>>>>> Richard > From steve.x.northover at oracle.com Wed Jan 4 12:05:06 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Wed, 04 Jan 2012 15:05:06 -0500 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1ADF64D6-BB8A-4E47-AA66-8DF2AC7CD67F@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <4F04AA7F.1050105@oracle.com> <33607435-87C5-48FB-B22C-FDC861353E0C@oracle.com> <1ADF64D6-BB8A-4E47-AA66-8DF2AC7CD67F@oracle.com> Message-ID: <4F04B0F2.3090600@oracle.com> The API is ugly and I'm not sure is does much other than stop tasks that are canceled from reporting progress. But I think that was the issue we were wondering about? If the only way to report progress is to (bogusly) "check for cancel, provide a status message, provide an amount worked" all at the same time, then I supposed you could still keep going and not return from the if-statement and the problem still exists (the task keep running and reporting progress even when canceled). So the issue remains: What to do when the tasks is canceled but doesn't stop reporting progress and status? Is suppose that we just let it keep going and leave the decision up to whoever writes the UI. At least they could gray out the cancel button so the user doesn't keep hammering on it ... Steve On 04/01/2012 2:42 PM, Richard Bair wrote: > Though once I went down this road, I thought "well, if it is opt-in, then it really isn't buying me much beyond just a runLater which we already have, and if it is opt-out it already breaks people", so I didn't pursue this farther. > > On Jan 4, 2012, at 11:40 AM, Richard Bair wrote: > >> Right, I wondered about that as well, though in the context of updateProgress(i, 1000, boolean???) or some other way to say "when I invoke this one, I mean it to allow updates after cancel, or not, or whatnot". >> >> >> On Jan 4, 2012, at 11:37 AM, steve.x.northover at oracle.com wrote: >> >>> >>> for (iterations=0; iterations<1000; iterations++) } >>> //ugly API but means that you can't update progress without checking for cancel or setting the message >>> if (isCancelled("Interation " + i, i) { >>> break; >>> } >>> ... do work ... >>> } >>> >>> >>> Steve >>> >>> On 04/01/2012 2:30 PM, Richard Bair wrote: >>>> What do you mean? >>>> >>>> On Jan 4, 2012, at 11:24 AM, steve.x.northover at oracle.com wrote: >>>> >>>>> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >>>>> >>>>> Steve >>>>> >>>>> On 04/01/2012 2:11 PM, Richard Bair wrote: >>>>>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>>>>> >>>>>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>>>>> >>>>>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>>>>> >>>>>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>>>>> >>>>>> Richard >>>>>> >>>>>> >>>>>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>>>>> >>>>>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>>>>> >>>>>>> Steve >>>>>>> >>>>>>> On 04/01/2012 2:00 PM, Richard Bair wrote: >>>>>>>> Today, they just work (they update the progress, title, message). >>>>>>>> >>>>>>>> Richard >>>>>>>> >>>>>>>> On Jan 4, 2012, at 10:59 AM, steve.x.northover at oracle.com wrote: >>>>>>>> >>>>>>>>> What happens now when updateProgress() and friends are called for a canceled task? >>>>>>>>> >>>>>>>>> Steve >>>>>>>>> >>>>>>>>> On 04/01/2012 1:55 PM, Richard Bair wrote: >>>>>>>>>> OK. The only regret I have here is that the fact the faulty code is not the fault of the developer per se, but rather the fault of Java for not allowing the killing of arbitrary threads (and I know we used to allow this and found major problems with it and thus deprecated it, I'm just sayin'). That is, the natural expectation for most people when they first approach it is going to be "hey, I cancelled this thing and it is still running! What the heck??". Then they need to learn more, understand the design space, read the documentation, etc. >>>>>>>>>> >>>>>>>>>> My proposed solution makes it work as expected most of the time, and puts the burden on the more sophisticated developers who want to update the progress or message from a background thread. However, since doing nothing but improving documentation means no potential breakage (which introducing an ISE could do), I'm OK with only updating the docs and calling it good :-). >>>>>>>>>> >>>>>>>>>> I have added a whole ream of additional samples to Task, and the very first example demonstrates how to do this correctly. Note that the samples team is going to need to update all their code to do this correctly. I haven't tried all these code samples in real life to make sure they all work, I will do so before committing. >>>>>>>>>> >>>>>>>>>> *

Examples

>>>>>>>>>> *

>>>>>>>>>> * The following set of examples demonstrate some of the most common uses of >>>>>>>>>> * Tasks. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

A Simple Loop

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * The first example is a simple loop that does nothing particularly useful, >>>>>>>>>> * but demonstrates the fundamental aspects of writing a Task correctly. This >>>>>>>>>> * example will simply loop and print to standard out on each loop iteration. >>>>>>>>>> * When it completes, it returns the number of times it iterated. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>>> *             int iterations = 0;
>>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>>> *                     break;
>>>>>>>>>> *                 }
>>>>>>>>>> *                 System.out.println("Iteration " + iterations);
>>>>>>>>>> *             }
>>>>>>>>>> *             return iterations;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * First, we define what type of value is returned from this Task. In this >>>>>>>>>> * case, we want to return the number of times we iterated, so we will >>>>>>>>>> * specify the Task to be of type Integer by using generics. Then, within >>>>>>>>>> * the implementation of thecall method, we iterate from >>>>>>>>>> * 0 to 1000. On each iteration, we check to see whether this Task has >>>>>>>>>> * been cancelled. If it has been, then we break out of the loop and return >>>>>>>>>> * the number of times we iterated. Otherwise a message is printed to >>>>>>>>>> * the console and the iteration count increased and we continue looping. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * Checking for isCancelled() in the loop body is critical, otherwise the >>>>>>>>>> * developer may cancel the task, but the task will continue running >>>>>>>>>> * and updating both the progress and returning the incorrect result >>>>>>>>>> * from the end of thecall method. A correct implementation >>>>>>>>>> * of a Task will always check for cancellation. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

A Simple Loop With Progress Notification

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * Similar to the previous example, except this time we will modify the >>>>>>>>>> * progress of the Task in each iteration. Note that we have a choice >>>>>>>>>> * to make in the case of cancellation. Do we want to set the progress back >>>>>>>>>> * to -1 (indeterminate) when the Task is cancelled, or do we want to leave >>>>>>>>>> * the progress where it was at? In this case, lets leave the progress alone >>>>>>>>>> * and only update the message on cancellation, though updating the >>>>>>>>>> * progress after cancellation is a perfectly valid choice. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>>> *             int iterations = 0;
>>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>>> *                     break;
>>>>>>>>>> *                 }
>>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>>> *                 (iterations, 1000);
>>>>>>>>>> *             }
>>>>>>>>>> *             return iterations;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * As before, within the for loop we check whether the Task has been >>>>>>>>>> * cancelled. If it has been cancelled, we will update the Task's >>>>>>>>>> * message to indicate that it has been cancelled, and then break as >>>>>>>>>> * before. If the Task has not been cancelled, then we will update its >>>>>>>>>> * message to indicate the current iteration and then update the >>>>>>>>>> * progress to indicate the current progress. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

A Simple Loop With Progress Notification And Blocking Calls

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * This example adds to the previous examples a blocking call. Because a >>>>>>>>>> * blocking call may thrown an InterruptedException, and because an >>>>>>>>>> * InterruptedException may occur as a result of the Task being cancelled, >>>>>>>>>> * we need to be sure to handle the InterruptedException and check on the >>>>>>>>>> * cancel state. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>>> *             int iterations = 0;
>>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>>> *                     break;
>>>>>>>>>> *                 }
>>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>>> *                 updateProgress(iterations, 1000);
>>>>>>>>>> *
>>>>>>>>>> *                 // Now block the thread for a short time, but be sure
>>>>>>>>>> *                 // to check the interrupted exception for cancellation!
>>>>>>>>>> *                 try {
>>>>>>>>>> *                     Thread.sleep(100);
>>>>>>>>>> *                 } catch (InterruptedException interrupted) {
>>>>>>>>>> *                     if (isCancelled()) {
>>>>>>>>>> *                         updateMessage("Cancelled");
>>>>>>>>>> *                         break;
>>>>>>>>>> *                     }
>>>>>>>>>> *                 }
>>>>>>>>>> *             }
>>>>>>>>>> *             return iterations;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * Here we have added to the body of the loop aThread.sleep >>>>>>>>>> * call. Since this is a blocking call, I have to handle the potential >>>>>>>>>> * InterruptedException. Within the catch block, I will check whether >>>>>>>>>> * the Task has been cancelled, and if so, update the message accordingly >>>>>>>>>> * and break out of the loop. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

A Task Which Takes Parameters

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * Most Tasks require some parameters in order to do useful work. For >>>>>>>>>> * example, a DeleteRecordTask needs the object or primary key to delete >>>>>>>>>> * from the database. A ReadFileTask needs the URI of the file to be read. >>>>>>>>>> * Because Tasks operate on a background thread, care must be taken to >>>>>>>>>> * make sure the body of thecall method does not read or >>>>>>>>>> * modify any shared state. There are two techniques most useful for >>>>>>>>>> * doing this: using final variables, and passing variables to a Task >>>>>>>>>> * during construction. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * When using a Task as an anonymous class, the most natural way to pass >>>>>>>>>> * parameters to the Task is by using final variables. In this example, >>>>>>>>>> * we pass to the Task the total number of times the Task should iterate. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     final int totalIterations = 900;
>>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>>> *             int iterations = 0;
>>>>>>>>>> *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>>> *                     break;
>>>>>>>>>> *                 }
>>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>>> *                 updateProgress(iterations, totalIterations);
>>>>>>>>>> *             }
>>>>>>>>>> *             return iterations;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * SincetotalIterations is final, thecall >>>>>>>>>> * method can safely read it and refer to it from a background thread. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * When writing Task libraries (as opposed to specific-use implementations), >>>>>>>>>> * we need to use a different technique. In this case, I will create an >>>>>>>>>> * IteratingTask which performs the same work as above. This time, since >>>>>>>>>> * the IteratingTask is defined in its own file, it will need to have >>>>>>>>>> * parameters passed to it in its constructor. These parameters are >>>>>>>>>> * assigned to final variables. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     public class IteratingTask extends Task<Integer> {
>>>>>>>>>> *         private final int totalIterations;
>>>>>>>>>> *
>>>>>>>>>> *         public IteratingTask(int totalIterations) {
>>>>>>>>>> *             this.totalIterations = totalIterations;
>>>>>>>>>> *         }
>>>>>>>>>> *
>>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>>> *             int iterations = 0;
>>>>>>>>>> *             for (iterations = 0; iterations< totalIterations; iterations++) {
>>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>>> *                     updateMessage("Cancelled");
>>>>>>>>>> *                     break;
>>>>>>>>>> *                 }
>>>>>>>>>> *                 updateMessage("Iteration " + iterations);
>>>>>>>>>> *                 updateProgress(iterations, totalIterations);
>>>>>>>>>> *             }
>>>>>>>>>> *             return iterations;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

And then when used:

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     IteratingTask task = new IteratingTask(800);
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

In this way, parameters are passed to the IteratingTask in a safe >>>>>>>>>> * manner, and again, are final. Thus, thecall method can >>>>>>>>>> * safely read this state from a background thread.

>>>>>>>>>> * >>>>>>>>>> *

WARNING: Do not pass mutable state to a Task and then operate on it >>>>>>>>>> * from a background thread. Doing so may introduce race conditions. In >>>>>>>>>> * particular, suppose you had a SaveCustomerTask which took a Customer >>>>>>>>>> * in its constructor. Although the SaveCustomerTask may have a final >>>>>>>>>> * reference to the Customer, if the Customer object is mutable, then it >>>>>>>>>> * is possible that both the SaveCustomerTask and some other application code >>>>>>>>>> * will be reading or modifying the state of the Customer from different >>>>>>>>>> * threads. Be very careful in such cases, that while a mutable object such >>>>>>>>>> * as this Customer is being used from a background thread, that it is >>>>>>>>>> * not being used also from another thread. In particular, if the background >>>>>>>>>> * thread is reading data from the database and updating the Customer object, >>>>>>>>>> * and the Customer object is bound to scene graph nodes (such as UI >>>>>>>>>> * controls), then there could be a violation of threading rules! For such >>>>>>>>>> * cases, modify the Customer object from the FX Application Thread rather >>>>>>>>>> * than from the background thread.

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     public class UpdateCustomerTask extends Task<Customer> {
>>>>>>>>>> *         private final Customer customer;
>>>>>>>>>> *
>>>>>>>>>> *         public UpdateCustomerTask(Customer customer) {
>>>>>>>>>> *             this.customer = customer;
>>>>>>>>>> *         }
>>>>>>>>>> *
>>>>>>>>>> *         @@Override protected Customer call() throws Exception {
>>>>>>>>>> *             // pseudo-code:
>>>>>>>>>> *             //   query the database
>>>>>>>>>> *             //   read the values
>>>>>>>>>> *
>>>>>>>>>> *             // Now update the customer
>>>>>>>>>> *             Platform.runLater(new Runnable() {
>>>>>>>>>> *                 @@Override public void run() {
>>>>>>>>>> *                     customer.firstName(rs.getString("FirstName"));
>>>>>>>>>> *                     // etc
>>>>>>>>>> *                 }
>>>>>>>>>> *             });
>>>>>>>>>> *
>>>>>>>>>> *             return customer;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

A Task Which Returns No Value

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * Many, if not most, Tasks should return a value upon completion. For >>>>>>>>>> * CRUD Tasks, one would expect that a "Create" Task would return the newly >>>>>>>>>> * created object or primary key, a "Read" Task would return the read >>>>>>>>>> * object, an "Update" task would return the number of records updated, >>>>>>>>>> * and a "Delete" task would return the number of records deleted. >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> * However sometimes there just isn't anything truly useful to return. >>>>>>>>>> * For example, I might have a Task which writes to a file. Task has built >>>>>>>>>> * into it a mechanism for indicating whether it has succeeded or failed >>>>>>>>>> * along with the number of bytes written (the progress), and thus there is >>>>>>>>>> * nothing really for me to return. In such a case, you can use the Void >>>>>>>>>> * type. This is a special type in the Java language which can only be >>>>>>>>>> * assigned the value ofnull. You would use it as follows: >>>>>>>>>> *

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     final String filePath = "/foo.txt";
>>>>>>>>>> *     final String contents = "Some contents";
>>>>>>>>>> *     Task<Void> task = new Task<Void>() {
>>>>>>>>>> *         @@Override protected Void call() throws Exception {
>>>>>>>>>> *             File file = new File(filePath);
>>>>>>>>>> *             FileOutputStream out = new FileOutputStream(file);
>>>>>>>>>> *             // ... and other code to write the contents ...
>>>>>>>>>> *
>>>>>>>>>> *             // Return null at the end of a Task of type Void
>>>>>>>>>> *             return null;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

A Task Which Returns An ObservableList

>>>>>>>>>> * >>>>>>>>>> *

Because the ListView, TableView, and other UI controls and scene graph >>>>>>>>>> * nodes make use of ObservableList, it is common to want to create and return >>>>>>>>>> * an ObservableList from a Task. When you do not care to display intermediate >>>>>>>>>> * values, the easiest way to correctly write such a Task is simply to >>>>>>>>>> * construct an ObservableList within thecall method, and then >>>>>>>>>> * return it at the conclusion of the Task.

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
>>>>>>>>>> *         @@Override protected ObservableList<Rectangle> call() throws Exception {
>>>>>>>>>> *             ObservableList<Rectangle> results = FXCollections.observableArrayList();
>>>>>>>>>> *             for (int i=0; i<100; i++) {
>>>>>>>>>> *                 if (isCancelled()) break;
>>>>>>>>>> *                 Rectangle r = new Rectangle(10, 10);
>>>>>>>>>> *                 r.setX(10 * i);
>>>>>>>>>> *                 results.add(r);
>>>>>>>>>> *             }
>>>>>>>>>> *             return results;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

In the above example, we are going to create 100 rectangles and return >>>>>>>>>> * them from this task. An ObservableList is created within the >>>>>>>>>> *call method, populated, and then returned.

>>>>>>>>>> * >>>>>>>>>> *

A Task Which Returns Partial Results

>>>>>>>>>> * >>>>>>>>>> *

Sometimes you want to create a Task which will return partial results. >>>>>>>>>> * Perhaps you are building a complex scene graph and want to show the >>>>>>>>>> * scene graph as it is being constructed. Or perhaps you are reading a large >>>>>>>>>> * amount of data over the network and want to display the entries in a >>>>>>>>>> * TableView as the data is arriving. In such cases, there is some shared state >>>>>>>>>> * available both to the FX Application Thread and the background thread. >>>>>>>>>> * Great care must be taken tonever update shared state from any >>>>>>>>>> * thread other than the FX Application Thread.

>>>>>>>>>> * >>>>>>>>>> *

The easiest way to do this is to expose a new property on the Task >>>>>>>>>> * which will represent the partial result. Then make sure to use >>>>>>>>>> *Platform.runLater when adding new items to the partial >>>>>>>>>> * result.

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
>>>>>>>>>> *         // Uses Java 7 diamond operator
>>>>>>>>>> *         private ReadOnlyObjectWrapper>      partialResults =
>>>>>>>>>> *             new ReadOnlyObjectWrapper<>(this, "partialResults");
>>>>>>>>>> *
>>>>>>>>>> *         public final ObservableList      getPartialResults() { return partialResults; }
>>>>>>>>>> *         public final ReadOnlyObjectProperty>      partialResultsProperty() {
>>>>>>>>>> *             return partialResults.getReadOnlyProperty();
>>>>>>>>>> *         }
>>>>>>>>>> *
>>>>>>>>>> *         @@Override protected ObservableList      call() throws Exception {
>>>>>>>>>> *             for (int i=0; i<100; i++) {
>>>>>>>>>> *                 if (isCancelled()) break;
>>>>>>>>>> *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>>>> *                 r.setX(10 * i);
>>>>>>>>>> *                 Platform.runLater(new Runnable() {
>>>>>>>>>> *                     @@Override public void run() {
>>>>>>>>>> *                         partialResults.add(r);
>>>>>>>>>> *                     }
>>>>>>>>>> *                 });
>>>>>>>>>> *             }
>>>>>>>>>> *             return partialResults;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> * >>>>>>>>>> * >>>>>>>>>> *

A Task Which Modifies The Scene Graph

>>>>>>>>>> * >>>>>>>>>> *

Generally, Tasks should not interact directly with the UI. Doing so >>>>>>>>>> * creates a tight coupling between a specific Task implementation and a >>>>>>>>>> * specific part of your UI. However, when you do want to create such a >>>>>>>>>> * coupling, you must ensure that you usePlatform.runLater >>>>>>>>>> * so that any modifications of the scene graph occur on the >>>>>>>>>> * FX Application Thread.

>>>>>>>>>> * >>>>>>>>>> *

>>>>>>>>>> *     final Group group = new Group();
>>>>>>>>>> *     Task<Void> task = new Task<Void>() {
>>>>>>>>>> *         @@Override protected Void call() throws Exception {
>>>>>>>>>> *             for (int i=0; i<100; i++) {
>>>>>>>>>> *                 if (isCancelled()) break;
>>>>>>>>>> *                 final Rectangle r = new Rectangle(10, 10);
>>>>>>>>>> *                 r.setX(10 * i);
>>>>>>>>>> *                 Platform.runLater(new Runnable() {
>>>>>>>>>> *                     @@Override public void run() {
>>>>>>>>>> *                         group.getChildren().add(r);
>>>>>>>>>> *                     }
>>>>>>>>>> *                 });
>>>>>>>>>> *             }
>>>>>>>>>> *             return null;
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> * >>>>>>>>>> *

Reacting To State Changes Generically

>>>>>>>>>> * >>>>>>>>>> *

Sometimes you may want to write a Task which updates its progress, >>>>>>>>>> * message, text, or in some other way reacts whenever a state change >>>>>>>>>> * happens on the Task. For example, you may want to change the status >>>>>>>>>> * message on the Task on Failure, Success, Running, or Cancelled state changes. >>>>>>>>>> *

>>>>>>>>>> *

>>>>>>>>>> *     Task<Integer> task = new Task<Integer>() {
>>>>>>>>>> *         @@Override protected Integer call() throws Exception {
>>>>>>>>>> *             int iterations = 0;
>>>>>>>>>> *             for (iterations = 0; iterations< 1000; iterations++) {
>>>>>>>>>> *                 if (isCancelled()) {
>>>>>>>>>> *                     break;
>>>>>>>>>> *                 }
>>>>>>>>>> *                 System.out.println("Iteration " + iterations);
>>>>>>>>>> *             }
>>>>>>>>>> *             return iterations;
>>>>>>>>>> *         }
>>>>>>>>>> *
>>>>>>>>>> *         @@Override protected void succeeded() {
>>>>>>>>>> *             super.succeeded();
>>>>>>>>>> *             updateMessage("Done!");
>>>>>>>>>> *         }
>>>>>>>>>> *
>>>>>>>>>> *         @@Override protected void cancelled() {
>>>>>>>>>> *             super.cancelled();
>>>>>>>>>> *             updateMessage("Cancelled!");
>>>>>>>>>> *         }
>>>>>>>>>> *
>>>>>>>>>> *         @@Override protected void failed() {
>>>>>>>>>> *             super.failed();
>>>>>>>>>> *             updateMessage("Failed!");
>>>>>>>>>> *         }
>>>>>>>>>> *     }
>>>>>>>>>> *
>>>>>>>>>> >>>>>>>>>> You'll notice that I also included a comment there about a PartialResultsTask. While writing the above it occurred to me that the partial results use case is not very elegant at the moment. I have some thoughts on that I was playing with which I'll post for review when I've thought about it more. >>>>>>>>>> >>>>>>>>>> So the current proposal for this issue is: close as not an issue, and improve the documentation as above. >>>>>>>>>> >>>>>>>>>> Thanks everybody for your very valuable input! >>>>>>>>>> Richard >>>>>>>>>> >>>>>>>>>> On Jan 4, 2012, at 7:49 AM, steve.x.northover at oracle.com wrote: >>>>>>>>>> >>>>>>>>>>> +1 for not fixing faulty code. People who write background task code need to play by the rules and check for their task being canceled. >>>>>>>>>>> >>>>>>>>>>> Steve >>>>>>>>>>> >>>>>>>>>>> On 04/01/2012 8:11 AM, Roman Kennke wrote: >>>>>>>>>>>> Hi Richard, >>>>>>>>>>>> >>>>>>>>>>>> I agree with the other's sentiments that JavaFX should not try to fix >>>>>>>>>>>> faulty code. >>>>>>>>>>>> >>>>>>>>>>>> I would lean to do more or less what is done in java.util.concurrent, >>>>>>>>>>>> especially the FutureTask class. I.e. provide an API to check >>>>>>>>>>>> isCancelled() and otherwise use InterruptionException to correctly >>>>>>>>>>>> interrupt a thread: >>>>>>>>>>>> >>>>>>>>>>>> http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html >>>>>>>>>>>> >>>>>>>>>>>> In my experience, APIs that let people write shitty code leads to a lot >>>>>>>>>>>> of shitty code being written (surprise!), especially when threads are >>>>>>>>>>>> involved. >>>>>>>>>>>> >>>>>>>>>>>> Cheers, Roman >>>>>>>>>>>> >>>>>>>>>>>> Am Dienstag, den 03.01.2012, 20:05 -0800 schrieb Richard Bair: >>>>>>>>>>>>> http://javafx-jira.kenai.com/browse/RT-17932 >>>>>>>>>>>>> >>>>>>>>>>>>> I have a proposed solution for this bug, if anybody has a few minutes to give it some thought that would be great. In a nutshell: >>>>>>>>>>>>> >>>>>>>>>>>>> - When a task is cancelled, the background thread might continue to operate. This is the way Java works (we can't safely kill a thread outright, so we have to wait for the thread to terminate) >>>>>>>>>>>>> - The developer of a Task can check isCancelled from the background thread and self-terminate >>>>>>>>>>>>> - The developer can use any API that throws an InterruptedException and use that to indicate that the thread has been cancelled >>>>>>>>>>>>> - A "run-away" background thread that is supposed to be cancelled, can continue to update the text, message, and progress of a task, giving the appearance that it isn't cancelled! >>>>>>>>>>>>> - A cancelled task might want to legitimately update the text, message, or progress in the case of cancellation (maybe the message becomes "I'm cancelling this request"). >>>>>>>>>>>>> >>>>>>>>>>>>> In trying to solve this problem, I wanted a solution where the naive implementation still works as one would expect, and a well considered solution works perfectly. I think I have a solution. >>>>>>>>>>>>> >>>>>>>>>>>>> If you write a Task, and do nothing smart: >>>>>>>>>>>>> >>>>>>>>>>>>> new Task() { >>>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>>> doSomething(); >>>>>>>>>>>>> } >>>>>>>>>>>>> return null; >>>>>>>>>>>>> } >>>>>>>>>>>>> }; >>>>>>>>>>>>> >>>>>>>>>>>>> In this case, if the task is cancelled, then it will just keep running in the background, but since the task returns nothing and never updates the progress or text or message, it appears to have been successfully cancelled to any code using the task. Here is another dumb implementation: >>>>>>>>>>>>> >>>>>>>>>>>>> new Task() { >>>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>>> doSomething(); >>>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>>> } >>>>>>>>>>>>> return null; >>>>>>>>>>>>> } >>>>>>>>>>>>> }; >>>>>>>>>>>>> >>>>>>>>>>>>> This one will update the progress, and exhibits the bug. My proposal is that calling updateProgress, updateMessage, or updateText from a background thread of a cancelled Task will result in an IllegalStateException. So in this case, the attempt to updateProgress after the task was cancelled will actually cause the exception and, in this case, actually terminate the background thread. Two birds, one stone. >>>>>>>>>>>>> >>>>>>>>>>>>> In this case...: >>>>>>>>>>>>> >>>>>>>>>>>>> new Task() { >>>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>>> try { >>>>>>>>>>>>> doSomething(); >>>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>>> } >>>>>>>>>>>>> return null; >>>>>>>>>>>>> } >>>>>>>>>>>>> }; >>>>>>>>>>>>> >>>>>>>>>>>>> ...we are not so lucky. Although the progress won't be updated on a cancelled task, it will keep running in the background. But no harm, no foul. If the developer was smart they'd do something like: >>>>>>>>>>>>> >>>>>>>>>>>>> new Task() { >>>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>>> if (isCancelled()) break; >>>>>>>>>>>>> try { >>>>>>>>>>>>> doSomething(); >>>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>>> } >>>>>>>>>>>>> return null; >>>>>>>>>>>>> } >>>>>>>>>>>>> }; >>>>>>>>>>>>> >>>>>>>>>>>>> Anyway, it seems that this solution doesn't hurt the naive case, and in some instances helps it, and in any case for more sophisticated library developers you have the tools to handle cancellation correctly. Here are two ways to correctly update the text after the task has been cancelled: >>>>>>>>>>>>> >>>>>>>>>>>>> new Task() { >>>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>>> if (isCancelled()) { >>>>>>>>>>>>> Platform.runLater(new Runnable() { >>>>>>>>>>>>> public void run() { >>>>>>>>>>>>> updateMessage("Cancelled") >>>>>>>>>>>>> } >>>>>>>>>>>>> }); >>>>>>>>>>>>> break; >>>>>>>>>>>>> } >>>>>>>>>>>>> try { >>>>>>>>>>>>> doSomething(); >>>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>>> } >>>>>>>>>>>>> return null; >>>>>>>>>>>>> } >>>>>>>>>>>>> }; >>>>>>>>>>>>> >>>>>>>>>>>>> new Task() { >>>>>>>>>>>>> public Object call() throws Exception { >>>>>>>>>>>>> for (int i=0; i<100; i++) { >>>>>>>>>>>>> if (isCancelled()) break; >>>>>>>>>>>>> try { >>>>>>>>>>>>> doSomething(); >>>>>>>>>>>>> updateProgress(i, 100); >>>>>>>>>>>>> } catch (Exception e) { } >>>>>>>>>>>>> } >>>>>>>>>>>>> return null; >>>>>>>>>>>>> } >>>>>>>>>>>>> >>>>>>>>>>>>> @Override protected void cancelled() { >>>>>>>>>>>>> updateMessage("Cancelled"); >>>>>>>>>>>>> } >>>>>>>>>>>>> }; >>>>>>>>>>>>> >>>>>>>>>>>>> I think this works fairly well. Anything I'm missing? >>>>>>>>>>>>> >>>>>>>>>>>>> Thanks! >>>>>>>>>>>>> Richard From hang.vo at oracle.com Wed Jan 4 14:12:16 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 04 Jan 2012 22:12:16 +0000 Subject: hg: openjfx/2.1/master/rt: fix .classpath Message-ID: <20120104221217.188C547882@hg.openjdk.java.net> Changeset: c626ee9fb52b Author: snorthov Date: 2012-01-04 17:05 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c626ee9fb52b fix .classpath ! .classpath From james.graham at oracle.com Wed Jan 4 17:26:52 2012 From: james.graham at oracle.com (Jim Graham) Date: Wed, 04 Jan 2012 17:26:52 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> Message-ID: <4F04FC5C.6060308@oracle.com> This was the original reason behind checked exceptions. IO operations were the main target because they are the thing that most threads end up in when they really should have been cancelled - and also because those methods are blocking and we needed an exceptional return value. Simply declaring updateProgress() and some other key methods to declare that they throw Interrupted will mean that the developer will be forced to opt in by virtue of having to catch the exception. It's worked reasonably well for Java I/O for a while, though I'll admit it is an annoyance for programmers - but it is a healthy annoyance... ...jim On 1/4/2012 11:39 AM, Jeff Martin wrote: > Another idea might be to throw the InterruptedException from a method called fireInterruptedException(), so savy rollback Tasks could override it to do nothing. > > jeff > > > On Jan 4, 2012, at 1:24 PM, steve.x.northover at oracle.com wrote: > >> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >> >> Steve >> >> On 04/01/2012 2:11 PM, Richard Bair wrote: >>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>> >>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>> >>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>> >>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>> >>> Richard >>> >>> >>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>> >>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>> >>>> Steve > From tbee at tbee.org Wed Jan 4 22:21:20 2012 From: tbee at tbee.org (Tom Eugelink) Date: Thu, 05 Jan 2012 07:21:20 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F04FC5C.6060308@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> Message-ID: <4F054160.7040309@tbee.org> I suggested that too (throwing a checked exception name TaskCancelledException), but would required an API change. I was wondering; where did the InterruptedException go in Task? It's the one thing that should be handled AFAIK. And an exception is the only clean way for a thread to clean up its monitors and stuff. The essence of this problem is a Java one, not a JavaFX one. Even though I like all the ideas, they still seem like a band-aid to me. OTOH, exceptions being thrown is not such a exceptional situation. Suppose the ISE is thrown after the progress values are set. One could get things like: try { if (isCancelled()) { setProgress(100%) // this throws exception, normally this would be a break or return } setProgress(counter) } finally { cleanup() } Or maybe a specialized method that does not throw the ISE xception setProgress(100%, false) setProgressNoISE(100%) setProgressToFinished() Tom On 2012-01-05 02:26, Jim Graham wrote: > This was the original reason behind checked exceptions. IO operations were the main target because they are the thing that most threads end up in when they really should have been cancelled - and also because those methods are blocking and we needed an exceptional return value. > > Simply declaring updateProgress() and some other key methods to declare that they throw Interrupted will mean that the developer will be forced to opt in by virtue of having to catch the exception. > > It's worked reasonably well for Java I/O for a while, though I'll admit it is an annoyance for programmers - but it is a healthy annoyance... > > ...jim > > On 1/4/2012 11:39 AM, Jeff Martin wrote: >> Another idea might be to throw the InterruptedException from a method called fireInterruptedException(), so savy rollback Tasks could override it to do nothing. >> >> jeff >> >> >> On Jan 4, 2012, at 1:24 PM, steve.x.northover at oracle.com wrote: >> >>> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >>> >>> Steve >>> >>> On 04/01/2012 2:11 PM, Richard Bair wrote: >>>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>>> >>>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>>> >>>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>>> >>>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>>> >>>> Richard >>>> >>>> >>>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>>> >>>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>>> >>>>> Steve >> > From tom.schindl at bestsolution.at Thu Jan 5 08:01:00 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Thu, 05 Jan 2012 17:01:00 +0100 Subject: Why is the windows runtime/sdk only provided as .exe In-Reply-To: <4F048053.6010107@oracle.com> References: <4F045E66.2020408@bestsolution.at> <4F047CCF.6080600@oracle.com> <4F047EA0.5060300@bestsolution.at> <4F048053.6010107@oracle.com> Message-ID: <4F05C93C.3050102@bestsolution.at> http://javafx-jira.kenai.com/browse/RT-18807 Tom Am 04.01.12 17:37, schrieb Kevin Rushforth: > Oh, maybe we don't actually release the SDK on the public web page > either (I knew we didn't release the runtime as a zip since we don't > even generate it internally). > > -- Kevin > > > Tom Schindl wrote: >> Hi Kevin, >> >> Well maybe I'm blind but for win32 the there is NO zip available from >> this page [1]. >> >> The only thing available currently as a zip is the OS-X SDK version >> which misses the win32 native libs not? I'll file a JIRA. >> >> [1]http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html >> >> >> Am 04.01.12 17:22, schrieb Kevin Rushforth: >> >>> Good question. The SDK is available as both an installer and a zip, and >>> we could consider make the runtime available as a zip file as well, so >>> please file a JIRA feature request for this. >>> >>> As for your other question, it is not currently possible to have 2.0.2 >>> and 2.1 instaled side-by-side. >>> >>> -- Kevin >>> >>> >>> Tom Schindl wrote: >>> >>>> Hi, >>>> >>>> Now that since 2.0.2 (and also 2.1) are redistributeable it might make >>>> sense to provide them also as simple ZIP-Files. >>>> >>>> I find it odd that if I want to package JavaFX with my product that I >>>> first have to install something only my system, navigate to the install >>>> dir and copy over the stuff my own project directory. >>>> >>>> Is it BTW possible to have 2.1 and 2.0.2 installed next to each other? I >>>> guess not which makes it hard to test with both versions on the same >>>> system, which would be made much more easy if provided as simple >>>> ZIP-Files. >>>> >>>> Would you mind providing JavaFX 2.1 binaries (and maybe also > 2.0.2) as >>>> a simple zip-File like you do it with 2.1 OS-X ones? >>>> >>>> Tom >>>> >>>> >>>> >> >> >> -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From tom.schindl at bestsolution.at Thu Jan 5 08:13:24 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Thu, 05 Jan 2012 17:13:24 +0100 Subject: Get informations about JavaFX-Version at runtime In-Reply-To: References: <4EFF14E1.60705@bestsolution.at> <4EFF18A5.8030206@bestsolution.at> Message-ID: <4F05CC24.2050209@bestsolution.at> http://javafx-jira.kenai.com/browse/RT-18808 Tom Am 31.12.11 17:09, schrieb Richard Bair: > Sure, do you want to file an RFE? > > Thanks > Richard > > On Dec 31, 2011, at 6:13 AM, Tom Schindl wrote: > >> Hi, >> >> Ok I found an internal class which also sets up the System-Properties >> but I'd like to request if we could get a class to query information >> about the current running application (Version, Rendering-Pipeline used, >> ...) >> >> Tom >> >> Am 31.12.11 14:57, schrieb Tom Schindl: >>> Hi, >>> >>> Is there some class which holds version informations one can easily >>> query to find out if running on 2.0, 2.1, ... ? >>> >>> Tom >>> >> >> >> -- >> B e s t S o l u t i o n . a t EDV Systemhaus GmbH >> ------------------------------------------------------------------------ >> tom schindl gesch?ftsf?hrer/CEO >> ------------------------------------------------------------------------ >> eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 >> http://www.BestSolution.at phone ++43 512 935834 -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From richard.bair at oracle.com Thu Jan 5 10:26:35 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 5 Jan 2012 10:26:35 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F054160.7040309@tbee.org> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> Message-ID: The InterruptedException is only thrown if somebody has a blocking call within the "call" method using streams or Thread.sleep or whatnot. Otherwise you get no notification in the call method itself but have to manually ask "is this cancelled?". If the update methods throw an exception (maybe even InterruptedException...?) then it provides a way for most tasks to know that the thing has been cancelled. But tasks that never update progress or the message or whatnot would still run free if they aren't checking cancelled. But there's nothing we can do there. I tend to agree with Jim that a checked exception would have been appropriate here. It forces people (annoys them too) to fess up to the fact that this thing might have been cancelled. However that will break people even more at this point (source level breakage). Another benefit to having the updateXXX methods throw an exception when called from a background thread of a cancelled task is when you write non-looping background code: doSomething(); updateProgress(1, 4); doSomething2(); updateProgress(2, 4); doSomething3(); updateProgress(3, 4); doSomething4(); updateProgress(4, 4); In such a case, having to check isCancelled after each doSomething before calling updateProgress is kind of a pain. Instead having an exception lets my code remain simple and easy to read. Ideally we would have a checked TaskCancelledException so that: try { doSomething(); updateProgress(1, 4); doSomething2(); updateProgress(2, 4); doSomething3(); updateProgress(3, 4); doSomething4(); updateProgress(4, 4); } catch (TaskCancelledException ex) { // do stuff to clean up // ... // Update the progress and message Platform.runLater(() -> { updateProgress(4, 4); updateMessage("Cancelled"); }); } However, I think it is too late to add a checked exception. But we could add the unchecked exception. It will still break some people, but probably not many? I worry about breaking anybody. However if we threw an unchecked exception I could still write the above code if I care about responding to the cancellation in the task, and if I don't, I just let the exception kill the thread and I'm happy as a clam. And if instead of a TaskCancelledException I just threw InterruptedException, then I have a single exception I catch as an indication of cancellation instead of potentially two exceptions. SO I think the decision has to be: 1) Throw an unchecked exception from updateXXX whenever called from background thread that has been cancelled (preference might be for InterruptedException) or 2) Simply document that you have to check for isCancelled and leave it to the developer Option #1 will perhaps lead to cleaner code, but it will break some people and will introduce a little bit of semantic complexity. Option #2 will result in more isCancelled checks in Task implementations, but breaks nobody. I have both options implemented, so it is just a matter of choosing :-). I'm torn. We really can't do #2 now and #1 later, because the documentation is going to say that you can call updateXXX after the thread is cancelled, so it really has to be a choice made now. Vote for favorite? #1 or #2? After the general approach is decided, specifics can be hashed. If this were before 2.0 I would prefer #1, right now I'm leery about breaking folks. Richard On Jan 4, 2012, at 10:21 PM, Tom Eugelink wrote: > > I suggested that too (throwing a checked exception name TaskCancelledException), but would required an API change. I was wondering; where did the InterruptedException go in Task? It's the one thing that should be handled AFAIK. And an exception is the only clean way for a thread to clean up its monitors and stuff. > > The essence of this problem is a Java one, not a JavaFX one. Even though I like all the ideas, they still seem like a band-aid to me. OTOH, exceptions being thrown is not such a exceptional situation. Suppose the ISE is thrown after the progress values are set. One could get things like: > > try > { > if (isCancelled()) > { > setProgress(100%) // this throws exception, normally this would be a break or return > } > setProgress(counter) > } > finally > { > cleanup() > } > > > Or maybe a specialized method that does not throw the ISE xception > setProgress(100%, false) > setProgressNoISE(100%) > setProgressToFinished() > > Tom > > > > On 2012-01-05 02:26, Jim Graham wrote: >> This was the original reason behind checked exceptions. IO operations were the main target because they are the thing that most threads end up in when they really should have been cancelled - and also because those methods are blocking and we needed an exceptional return value. >> >> Simply declaring updateProgress() and some other key methods to declare that they throw Interrupted will mean that the developer will be forced to opt in by virtue of having to catch the exception. >> >> It's worked reasonably well for Java I/O for a while, though I'll admit it is an annoyance for programmers - but it is a healthy annoyance... >> >> ...jim >> >> On 1/4/2012 11:39 AM, Jeff Martin wrote: >>> Another idea might be to throw the InterruptedException from a method called fireInterruptedException(), so savy rollback Tasks could override it to do nothing. >>> >>> jeff >>> >>> >>> On Jan 4, 2012, at 1:24 PM, steve.x.northover at oracle.com wrote: >>> >>>> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >>>> >>>> Steve >>>> >>>> On 04/01/2012 2:11 PM, Richard Bair wrote: >>>>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>>>> >>>>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>>>> >>>>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>>>> >>>>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>>>> >>>>> Richard >>>>> >>>>> >>>>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>>>> >>>>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>>>> >>>>>> Steve >>> >> > > From roman at kennke.org Thu Jan 5 12:29:25 2012 From: roman at kennke.org (Roman Kennke) Date: Thu, 05 Jan 2012 21:29:25 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> Message-ID: <1325795365.7469.24.camel@moonlight> Hi there, > The InterruptedException is only thrown if somebody has a blocking call within the "call" method using streams or Thread.sleep or whatnot. Otherwise you get no notification in the call method itself but have to manually ask "is this cancelled?". Interestingly, I/O streams are one of the things that are not interruptible (i.e. don't throw InterruptedException, and when you call Thread.interrupt() on a thread that's in an IO, it won't do anything). You can get interuptible IO only by using NIO. You can still check Thread.isInterrupted() though (but only after the IO operation unblocks). > If the update methods throw an exception (maybe even InterruptedException...?) then it provides a way for most tasks to know that the thing has been cancelled. But tasks that never update progress or the message or whatnot would still run free if they aren't checking cancelled. But there's nothing we can do there. > > I tend to agree with Jim that a checked exception would have been appropriate here. It forces people (annoys them too) to fess up to the fact that this thing might have been cancelled. However that will break people even more at this point (source level breakage). > > Another benefit to having the updateXXX methods throw an exception when called from a background thread of a cancelled task is when you write non-looping background code: > > doSomething(); > updateProgress(1, 4); > doSomething2(); > updateProgress(2, 4); > doSomething3(); > updateProgress(3, 4); > doSomething4(); > updateProgress(4, 4); > > > In such a case, having to check isCancelled after each doSomething before calling updateProgress is kind of a pain. Instead having an exception lets my code remain simple and easy to read. Ideally we would have a checked TaskCancelledException so that: > > try { > doSomething(); > updateProgress(1, 4); > doSomething2(); > updateProgress(2, 4); > doSomething3(); > updateProgress(3, 4); > doSomething4(); > updateProgress(4, 4); > } catch (TaskCancelledException ex) { > // do stuff to clean up > // ... > > // Update the progress and message > Platform.runLater(() -> { > updateProgress(4, 4); > updateMessage("Cancelled"); > }); > } > > However, I think it is too late to add a checked exception. But we could add the unchecked exception. It will still break some people, but probably not many? I worry about breaking anybody. However if we threw an unchecked exception I could still write the above code if I care about responding to the cancellation in the task, and if I don't, I just let the exception kill the thread and I'm happy as a clam. And if instead of a TaskCancelledException I just threw InterruptedException, then I have a single exception I catch as an indication of cancellation instead of potentially two exceptions. > > SO I think the decision has to be: > > 1) Throw an unchecked exception from updateXXX whenever called from background thread that has been cancelled (preference might be for InterruptedException) > > or > > 2) Simply document that you have to check for isCancelled and leave it to the developer Another option would be to call Thread.interrupt() to signal that the thread has been interrupted. Any call to a method that is blocking would throw an InterruptedException then. App code can check Thread.isInterrupted() if it wants to bail out even if it doesn't call blocking code. I am not even sure that we need a notion of 'isCancelled' if the interrupted flag does exactly this. On the other hand, we might want to use the interrupted flag internally, and - for example - throw an unchecked TaskInterruptedException from JavaFX code, as well as blowing up in blocking calls with InterruptedException. What do you think? Regards, Roman From richard.bair at oracle.com Thu Jan 5 12:36:49 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 5 Jan 2012 12:36:49 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1325795365.7469.24.camel@moonlight> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> Message-ID: <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> >> The InterruptedException is only thrown if somebody has a blocking call within the "call" method using streams or Thread.sleep or whatnot. Otherwise you get no notification in the call method itself but have to manually ask "is this cancelled?". > > Interestingly, I/O streams are one of the things that are not > interruptible (i.e. don't throw InterruptedException, and when you call > Thread.interrupt() on a thread that's in an IO, it won't do anything). > You can get interuptible IO only by using NIO. You can still check > Thread.isInterrupted() though (but only after the IO operation > unblocks). Huh, I don't know where I picked that up from. I just googled though and found: http://docs.oracle.com/javase/1.4.2/docs/api/java/io/InterruptedIOException.html So it looks like an InterruptedIOException is thrown, not an InterruptedException. Drat, another potential exception type that gets thrown when the thing is cancelled :-( >> If the update methods throw an exception (maybe even InterruptedException...?) then it provides a way for most tasks to know that the thing has been cancelled. But tasks that never update progress or the message or whatnot would still run free if they aren't checking cancelled. But there's nothing we can do there. >> >> I tend to agree with Jim that a checked exception would have been appropriate here. It forces people (annoys them too) to fess up to the fact that this thing might have been cancelled. However that will break people even more at this point (source level breakage). >> >> Another benefit to having the updateXXX methods throw an exception when called from a background thread of a cancelled task is when you write non-looping background code: >> >> doSomething(); >> updateProgress(1, 4); >> doSomething2(); >> updateProgress(2, 4); >> doSomething3(); >> updateProgress(3, 4); >> doSomething4(); >> updateProgress(4, 4); >> >> >> In such a case, having to check isCancelled after each doSomething before calling updateProgress is kind of a pain. Instead having an exception lets my code remain simple and easy to read. Ideally we would have a checked TaskCancelledException so that: >> >> try { >> doSomething(); >> updateProgress(1, 4); >> doSomething2(); >> updateProgress(2, 4); >> doSomething3(); >> updateProgress(3, 4); >> doSomething4(); >> updateProgress(4, 4); >> } catch (TaskCancelledException ex) { >> // do stuff to clean up >> // ... >> >> // Update the progress and message >> Platform.runLater(() -> { >> updateProgress(4, 4); >> updateMessage("Cancelled"); >> }); >> } >> >> However, I think it is too late to add a checked exception. But we could add the unchecked exception. It will still break some people, but probably not many? I worry about breaking anybody. However if we threw an unchecked exception I could still write the above code if I care about responding to the cancellation in the task, and if I don't, I just let the exception kill the thread and I'm happy as a clam. And if instead of a TaskCancelledException I just threw InterruptedException, then I have a single exception I catch as an indication of cancellation instead of potentially two exceptions. >> >> SO I think the decision has to be: >> >> 1) Throw an unchecked exception from updateXXX whenever called from background thread that has been cancelled (preference might be for InterruptedException) >> >> or >> >> 2) Simply document that you have to check for isCancelled and leave it to the developer > > Another option would be to call Thread.interrupt() to signal that the > thread has been interrupted. Any call to a method that is blocking would > throw an InterruptedException then. App code can check > Thread.isInterrupted() if it wants to bail out even if it doesn't call > blocking code. I am not even sure that we need a notion of 'isCancelled' > if the interrupted flag does exactly this. On the other hand, we might > want to use the interrupted flag internally, and - for example - throw > an unchecked TaskInterruptedException from JavaFX code, as well as > blowing up in blocking calls with InterruptedException. > > What do you think? I actually tried to unit test that and it didn't work. The implementation of cancel is handled by the concurrency libraries (FutureTask.cancel to be precise). I checked the thread interrupt status but it said it was not interrupted even after a cancel. But in any case, isCancelled is already there, it is part of the FutureTask API. Cheers Richard From hang.vo at oracle.com Thu Jan 5 12:42:05 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 05 Jan 2012 20:42:05 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120105204206.A4EED478A9@hg.openjdk.java.net> Changeset: edee8e730534 Author: Kinsley Wong Date: 2012-01-05 12:34 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/edee8e730534 RT-18644: ENTER in linux doesn't work for amount of controls. ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ButtonBehavior.java Changeset: 4eb295158a38 Author: Kinsley Wong Date: 2012-01-05 12:35 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/4eb295158a38 RT-18678: Additional fixes for Accordion TitledPane height is not updated when Accordion ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TitledPaneSkin.java From steve.x.northover at oracle.com Thu Jan 5 12:44:58 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Thu, 05 Jan 2012 15:44:58 -0500 Subject: Review of solution for Cancelled Tasks In-Reply-To: References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> Message-ID: <4F060BCA.3070708@oracle.com> If 2) ("Simply document that you have to check for isCancelled and leave it to the developer") - what about updateXXX()? You can call it but what does it do - does it actually update anything? Probably should (it's canceled ... but work is being done) - if it updates, then whatever UI is put on top of the task can decide how progress is shown for a canceled task that won't cancel Steve On 05/01/2012 1:26 PM, Richard Bair wrote: > The InterruptedException is only thrown if somebody has a blocking call within the "call" method using streams or Thread.sleep or whatnot. Otherwise you get no notification in the call method itself but have to manually ask "is this cancelled?". > > If the update methods throw an exception (maybe even InterruptedException...?) then it provides a way for most tasks to know that the thing has been cancelled. But tasks that never update progress or the message or whatnot would still run free if they aren't checking cancelled. But there's nothing we can do there. > > I tend to agree with Jim that a checked exception would have been appropriate here. It forces people (annoys them too) to fess up to the fact that this thing might have been cancelled. However that will break people even more at this point (source level breakage). > > Another benefit to having the updateXXX methods throw an exception when called from a background thread of a cancelled task is when you write non-looping background code: > > doSomething(); > updateProgress(1, 4); > doSomething2(); > updateProgress(2, 4); > doSomething3(); > updateProgress(3, 4); > doSomething4(); > updateProgress(4, 4); > > > In such a case, having to check isCancelled after each doSomething before calling updateProgress is kind of a pain. Instead having an exception lets my code remain simple and easy to read. Ideally we would have a checked TaskCancelledException so that: > > try { > doSomething(); > updateProgress(1, 4); > doSomething2(); > updateProgress(2, 4); > doSomething3(); > updateProgress(3, 4); > doSomething4(); > updateProgress(4, 4); > } catch (TaskCancelledException ex) { > // do stuff to clean up > // ... > > // Update the progress and message > Platform.runLater(() -> { > updateProgress(4, 4); > updateMessage("Cancelled"); > }); > } > > However, I think it is too late to add a checked exception. But we could add the unchecked exception. It will still break some people, but probably not many? I worry about breaking anybody. However if we threw an unchecked exception I could still write the above code if I care about responding to the cancellation in the task, and if I don't, I just let the exception kill the thread and I'm happy as a clam. And if instead of a TaskCancelledException I just threw InterruptedException, then I have a single exception I catch as an indication of cancellation instead of potentially two exceptions. > > SO I think the decision has to be: > > 1) Throw an unchecked exception from updateXXX whenever called from background thread that has been cancelled (preference might be for InterruptedException) > > or > > 2) Simply document that you have to check for isCancelled and leave it to the developer > > Option #1 will perhaps lead to cleaner code, but it will break some people and will introduce a little bit of semantic complexity. Option #2 will result in more isCancelled checks in Task implementations, but breaks nobody. > > I have both options implemented, so it is just a matter of choosing :-). I'm torn. We really can't do #2 now and #1 later, because the documentation is going to say that you can call updateXXX after the thread is cancelled, so it really has to be a choice made now. > > Vote for favorite? #1 or #2? After the general approach is decided, specifics can be hashed. If this were before 2.0 I would prefer #1, right now I'm leery about breaking folks. > > Richard > > On Jan 4, 2012, at 10:21 PM, Tom Eugelink wrote: > >> I suggested that too (throwing a checked exception name TaskCancelledException), but would required an API change. I was wondering; where did the InterruptedException go in Task? It's the one thing that should be handled AFAIK. And an exception is the only clean way for a thread to clean up its monitors and stuff. >> >> The essence of this problem is a Java one, not a JavaFX one. Even though I like all the ideas, they still seem like a band-aid to me. OTOH, exceptions being thrown is not such a exceptional situation. Suppose the ISE is thrown after the progress values are set. One could get things like: >> >> try >> { >> if (isCancelled()) >> { >> setProgress(100%) // this throws exception, normally this would be a break or return >> } >> setProgress(counter) >> } >> finally >> { >> cleanup() >> } >> >> >> Or maybe a specialized method that does not throw the ISE xception >> setProgress(100%, false) >> setProgressNoISE(100%) >> setProgressToFinished() >> >> Tom >> >> >> >> On 2012-01-05 02:26, Jim Graham wrote: >>> This was the original reason behind checked exceptions. IO operations were the main target because they are the thing that most threads end up in when they really should have been cancelled - and also because those methods are blocking and we needed an exceptional return value. >>> >>> Simply declaring updateProgress() and some other key methods to declare that they throw Interrupted will mean that the developer will be forced to opt in by virtue of having to catch the exception. >>> >>> It's worked reasonably well for Java I/O for a while, though I'll admit it is an annoyance for programmers - but it is a healthy annoyance... >>> >>> ...jim >>> >>> On 1/4/2012 11:39 AM, Jeff Martin wrote: >>>> Another idea might be to throw the InterruptedException from a method called fireInterruptedException(), so savy rollback Tasks could override it to do nothing. >>>> >>>> jeff >>>> >>>> >>>> On Jan 4, 2012, at 1:24 PM, steve.x.northover at oracle.com wrote: >>>> >>>>> One (wild/bogus/too late?) idea might be to combine checking for cancel with progress update. That way, programmer can't do one without the other. >>>>> >>>>> Steve >>>>> >>>>> On 04/01/2012 2:11 PM, Richard Bair wrote: >>>>>> Ya, that was exactly what I was proposing :-). If the updateXXX methods throw an ISE when called from a background thread of a cancelled task, then you will generally get the behavior you instinctively want -- the thread dies (most of the time) and the thing stops getting updated. >>>>>> >>>>>> However this means that when you actually DO want to update the progress, message, or title when the thing is cancelled, then you need to do it in the cancelled() method (which is called on the FX app thread) or you have to use a Platform.runLater() if you handle it from the call() method. >>>>>> >>>>>> So throwing the exceptions will cause some exceptions in existing apps that weren't happening before (although maybe that is good since it will, if left unhandled, kill the thread). It will make it more difficult for people who are wanting to update the title / message / progress after the thing is cancelled. >>>>>> >>>>>> In either case all use cases are possible, it is just a matter of which you bias for. Do you bias for the naive implementation or the sophisticated implementation? Generally I always bias for the naive implementation because even great developers are happier when things "just work" the way their intuition dictates and the exceptions give enough context that developers who want to do the advanced stuff can determine how it is done. >>>>>> >>>>>> Richard >>>>>> >>>>>> >>>>>> On Jan 4, 2012, at 11:05 AM, steve.x.northover at oracle.com wrote: >>>>>> >>>>>>> Hmmm .. perhaps they should throw an exception, not update the progress and not cancel nicely? I suppose that this "cancels" the task! >>>>>>> >>>>>>> Steve >> From richard.bair at oracle.com Thu Jan 5 13:13:05 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 5 Jan 2012 13:13:05 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F060BCA.3070708@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <4F060BCA.3070708@oracle.com> Message-ID: <2839245B-24B2-4355-A706-37CE9E8ECAAB@oracle.com> > If 2) ("Simply document that you have to check for isCancelled and leave it to the developer") > - what about updateXXX()? You can call it but what does it do > - does it actually update anything? Probably should (it's canceled ... but work is being done) Yes, calling it will actually update the message / progress / title > - if it updates, then whatever UI is put on top of the task can decide how progress is shown for a canceled task that won't cancel Right, they can have a more complicated binding such that: progressIndicator.progressProperty().bind( Bindings.when(task.runningProperty()).then(task.progressProperty()).otherwise(-1) ); Although they can do that with option #1 too. Richard From roman at kennke.org Thu Jan 5 13:21:46 2012 From: roman at kennke.org (Roman Kennke) Date: Thu, 05 Jan 2012 22:21:46 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> Message-ID: <1325798506.7469.54.camel@moonlight> Hi Richard, > >> The InterruptedException is only thrown if somebody has a blocking call within the "call" method using streams or Thread.sleep or whatnot. Otherwise you get no notification in the call method itself but have to manually ask "is this cancelled?". > > > > Interestingly, I/O streams are one of the things that are not > > interruptible (i.e. don't throw InterruptedException, and when you call > > Thread.interrupt() on a thread that's in an IO, it won't do anything). > > You can get interuptible IO only by using NIO. You can still check > > Thread.isInterrupted() though (but only after the IO operation > > unblocks). > > Huh, I don't know where I picked that up from. I just googled though and found: > > http://docs.oracle.com/javase/1.4.2/docs/api/java/io/InterruptedIOException.html > So it looks like an InterruptedIOException is thrown, not an InterruptedException. Drat, another potential exception type that gets thrown when the thing is cancelled :-( In my experience, when you call Thread.interrupt() on a thread that is blocked in a network IO, it will not throw this exception. The only way to 'cancel' such IO is to close() the stream, which will result in an IOException in the blocked thread. I am not sure what this exception is actually used for. Some stuff I found in the web seems to indicate it's only used for PipedStreams: http://docstore.mik.ua/orelly/java/fclass/ch11_30.htm In order to be sure, we probably need to consult an IO expert or check the java.io code ourselves, but I am pretty sure that at least network streams are not interruptible with java.io streams (might be different for files or pipes or whatnot). Which was the whole point why InterruptibleChannels have been introduced in NIO. > > Another option would be to call Thread.interrupt() to signal that the > > thread has been interrupted. Any call to a method that is blocking would > > throw an InterruptedException then. App code can check > > Thread.isInterrupted() if it wants to bail out even if it doesn't call > > blocking code. I am not even sure that we need a notion of 'isCancelled' > > if the interrupted flag does exactly this. On the other hand, we might > > want to use the interrupted flag internally, and - for example - throw > > an unchecked TaskInterruptedException from JavaFX code, as well as > > blowing up in blocking calls with InterruptedException. > > > > What do you think? > > I actually tried to unit test that and it didn't work. The implementation of cancel is handled by the concurrency libraries (FutureTask.cancel to be precise). I checked the thread interrupt status but it said it was not interrupted even after a cancel. But in any case, isCancelled is already there, it is part of the FutureTask API. Interesting. Well I guess the semantics is somewhat different: - Cancel means to signal a task (that might hang out on a queue until it gets processed) that its execution should not be started. If it's already started, only cancel(true) *can* stop it (if the task handles interruption). It does not provide a mechanism to roll up a thread. - Interruption is a mechanism to cleanly terminate an already running task. This is completely opt-in, i.e. the task must be implemented to actually handle thread interruption, e.g. when you have a long-running loop that calculates stuff (i.e. it is blocking), and you want to make it interruptible, you need to check Thread.isInterrupted() inside the loop and either throw InterruptedException or handle the interruption correctly otherwise. It's a bit complicated to get right, I always refer to this excellent article: http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html I guess the question to ask in our case is: 1. is updateXXX() blocking? No it is not, as far as I can see (correct me if I'm wrong). Therefore it should not throw InterruptedException or handle interruption in any way. Further, because InterruptedException is checked, and you already pointed out that we can't add a checked exception, this is not an option anyway. 2. How do we want to handle cancellation then: >> try { >> doSomething(); >> updateProgress(1, 4); >> doSomething2(); >> updateProgress(2, 4); >> doSomething3(); >> updateProgress(3, 4); >> doSomething4(); >> updateProgress(4, 4); >> } catch (TaskCancelledException ex) { >> // do stuff to clean up >> // ... >> >> // Update the progress and message >> Platform.runLater(() -> { >> updateProgress(4, 4); >> updateMessage("Cancelled"); >> }); >> } I think in general this is fine. But implementation-wise, how do you make updateProgress() throw TaskCancelledException in the try block, while not doing the same in the catch block? I like this approach (that you sent in your first email): new Task() { public Object call() throws Exception { for (int i=0; i<100; i++) { if (isCancelled()) break; try { doSomething(); updateProgress(i, 100); } catch (Exception e) { } } return null; } @Override protected void cancelled() { updateMessage("Cancelled"); } }; The cancelled() method would be called as soon as the task gets cancelled, where the application can perform the necessary cleanup, call updateProgress() or whatever without blowing up. Maybe this should be combined with throwing the TaskCancelledException (but only *after* the above call to cancelled() returned) to signal that the task can stop doing whatever it is doing, and roll up the executing thread. What do you think? Cheers, Roman From richard.bair at oracle.com Thu Jan 5 13:48:00 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 5 Jan 2012 13:48:00 -0800 Subject: Testing keyboard events in controls In-Reply-To: <1325679053.3443.2.camel@moonlight> References: <4F03EBA6.1080508@oracle.com> <1325679053.3443.2.camel@moonlight> Message-ID: <936CD761-21C3-4A14-934C-6D39CAB58C69@oracle.com> Our SQE organization has JemmyFX, built on Jemmy which is like FEST but different. Same basic principle I think. We're hoping to get that into openjfx this month... On Jan 4, 2012, at 4:10 AM, Roman Kennke wrote: > Hi Jonathan, > > Happy New Year to you and the whole JFX team! > > Not having looked at your code yet, I am wondering if it would make > sense to add a (java.awt.)Robot-like class to JavaFX? Maybe the AWT > Robot can even be used directly? (after all, it generates OS level > events, I believe it doesn't matter if it's an AWT window, JavaFX window > or any other window). > > A while ago I was thinking of building a FEST (for Swing) like testing > framework for JavaFX (for the ThingsFX project), and I'll certainly > start this once I have some time again. This should also be useful for > testing inside JavaFX itself. > > Cheers, Roman > > Am Mittwoch, den 04.01.2012, 16:03 +1000 schrieb Jonathan Giles: >> Hi all (and happy new year!), >> >> ListView/TreeView/TableView have a heap of keyboard requirements that >> come in from UX. Keyboard event handling code is often fraught with >> complexity due to the many permutations. I'm frequently getting burnt >> due to subtle changes in this code causing unintended regressions. >> >> I finally decided to do something about this, and have thrown together a >> very simple set of APIs to make it possible to write simple unit tests >> that test keyboard navigation. It's all very lightweight so that it can >> easily run as part of the JavaFX engineering continuous build (which >> means it fails sooner, compared to SQE tests, or even worse - when it >> ends up in the hands of developers!). Additionally, it's all very >> primitive and proof-of-concept at this stage, and I am happy to refine >> (and relocate) the class to provide utility in this area if it doesn't >> duplicate existing functionality I'm unaware of. I'm also certain I'm >> missing some of the details on how best to do this, so feedback is welcome. >> >> Anywho, you can find the class in the rt/javafx-ui-controls project, in >> the test directory, in javafx.scene.control.KeyEventFirer. You can see a >> few unit tests that were used to flesh out the API in >> javafx.scene.control.ListViewKeyInputTest. I plan to add many more here, >> and for other controls, in the coming weeks and months. >> >> These are the key pointers: >> 1) When creating a KeyEventFirer, you must provide an EventTarget. >> Despite the scary name, all Nodes are EventTargets. >> 2) I put the ListView inside a group, which is placed in a scene, which >> itself is placed in a stage. I show() and hide() the stage as necessary >> (in the setup() / tearDown() methods). Without this, events don't fire. >> 3) I use the separate javafx.scene.control.KeyModifier class to provide >> zero or more keyboard modifiers (shift, alt, ctrl, meta) to the keyboard >> input. An enum might already exist for these modifiers, but I'm not sure... >> 4) I have convenience methods (for my needs, anyway) for >> up/down/left/right keyboard inputs, in the form of >> 'do*ArrowPress(KeyModifier... modifiers)' methods. I'm sure more can be >> added... >> 5) For all other keyboard input, you can use the 'doKeyPress(KeyCode >> keyCode, KeyModifier... modifiers)' method. >> >> Any feedback would be appreciated. >> > > From tbee at tbee.org Thu Jan 5 13:53:09 2012 From: tbee at tbee.org (Tom Eugelink) Date: Thu, 05 Jan 2012 22:53:09 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1325798506.7469.54.camel@moonlight> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> Message-ID: <4F061BC5.7030606@tbee.org> On 2012-01-05 22:21, Roman Kennke wrote: > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > > @Override protected void cancelled() { > updateMessage("Cancelled"); > } > }; > > The cancelled() method would be called as soon as the task gets > cancelled, where the application can perform the necessary cleanup, call > updateProgress() or whatever without blowing up. > > Maybe this should be combined with throwing the TaskCancelledException > (but only *after* the above call to cancelled() returned) to signal that > the task can stop doing whatever it is doing, and roll up the executing > thread. > I must admit that I've lost the discussion somewhere. But seeing the code above, with the if(isCancelled()) check to break the loop, I do not see any need to throw an exception. The Task, after returing from call(), can check itself for isCancelled() and call the cancelled() method in the EDT (or whatever) for update. So, why an exception? Tom From richard.bair at oracle.com Thu Jan 5 14:04:07 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 5 Jan 2012 14:04:07 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1325798506.7469.54.camel@moonlight> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> Message-ID: Hi Roman! > 1. is updateXXX() blocking? No it is not, as far as I can see (correct > me if I'm wrong). Therefore it should not throw InterruptedException or > handle interruption in any way. Further, because InterruptedException is > checked, and you already pointed out that we can't add a checked > exception, this is not an option anyway. Right. updateXXX is not blocking, and I hadn't thought of checking whether InterruptedException is checked. > 2. How do we want to handle cancellation then: > >>> try { >>> doSomething(); >>> updateProgress(1, 4); >>> doSomething2(); >>> updateProgress(2, 4); >>> doSomething3(); >>> updateProgress(3, 4); >>> doSomething4(); >>> updateProgress(4, 4); >>> } catch (TaskCancelledException ex) { >>> // do stuff to clean up >>> // ... >>> >>> // Update the progress and message >>> Platform.runLater(() -> { >>> updateProgress(4, 4); >>> updateMessage("Cancelled"); >>> }); >>> } > > I think in general this is fine. But implementation-wise, how do you > make updateProgress() throw TaskCancelledException in the try block, > while not doing the same in the catch block? It is trivial. In the updateProgress method, I just do two checks: isCancelled() and a thread check. If the thread is not the FX App thread and isCancelled(), then throw an exception. In the above code I'm using Platform.runLater to run the updateProgress and updateMessage on the FX app thread, where it would still be legal. > I like this approach (that you sent in your first email): > > new Task() { > public Object call() throws Exception { > for (int i=0; i<100; i++) { > if (isCancelled()) break; > try { > doSomething(); > updateProgress(i, 100); > } catch (Exception e) { } > } > return null; > } > > @Override protected void cancelled() { > updateMessage("Cancelled"); > } > }; Ya, I like this too and would do it this way in most cases. However what about the case where you want one message on cancel if it happens during the first part of your task, but another message later in the task? Like "Cancelled while connecting" vs. "Cancelled while downloading data". In such a case you would need to have some thread safe state to communicate what you are doing so that from the cancelled method you can post the right message (note that cancelled() is called on the FX App thread, which is why it is safe to call updateMessage from there). > The cancelled() method would be called as soon as the task gets > cancelled, where the application can perform the necessary cleanup, call > updateProgress() or whatever without blowing up. > > Maybe this should be combined with throwing the TaskCancelledException > (but only *after* the above call to cancelled() returned) to signal that > the task can stop doing whatever it is doing, and roll up the executing > thread. I see, so in this situation you would have cancelled be called on the background thread as opposed to the fx thread. I think I like it better when called on the FX thread so that only a single method, "call" is invoked on a background thread all others on the FX thread. This way developers know that only this one method will be called by the system on a background thread. Cheers Richard From roman at kennke.org Thu Jan 5 14:45:35 2012 From: roman at kennke.org (Roman Kennke) Date: Thu, 05 Jan 2012 23:45:35 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> Message-ID: <1325803535.7469.73.camel@moonlight> Hi Richard, > > 2. How do we want to handle cancellation then: > > > >>> try { > >>> doSomething(); > >>> updateProgress(1, 4); > >>> doSomething2(); > >>> updateProgress(2, 4); > >>> doSomething3(); > >>> updateProgress(3, 4); > >>> doSomething4(); > >>> updateProgress(4, 4); > >>> } catch (TaskCancelledException ex) { > >>> // do stuff to clean up > >>> // ... > >>> > >>> // Update the progress and message > >>> Platform.runLater(() -> { > >>> updateProgress(4, 4); > >>> updateMessage("Cancelled"); > >>> }); > >>> } > > > > I think in general this is fine. But implementation-wise, how do you > > make updateProgress() throw TaskCancelledException in the try block, > > while not doing the same in the catch block? > > It is trivial. In the updateProgress method, I just do two checks: isCancelled() and a thread check. If the thread is not the FX App thread and isCancelled(), then throw an exception. In the above code I'm using Platform.runLater to run the updateProgress and updateMessage on the FX app thread, where it would still be legal. Uhm but it requires a specific pattern which is not obvious: use runLater() in the catch handler and not use runLater() in the try block. Good luck explaining that to developers! ;-) > > I like this approach (that you sent in your first email): > > > > new Task() { > > public Object call() throws Exception { > > for (int i=0; i<100; i++) { > > if (isCancelled()) break; > > try { > > doSomething(); > > updateProgress(i, 100); > > } catch (Exception e) { } > > } > > return null; > > } > > > > @Override protected void cancelled() { > > updateMessage("Cancelled"); > > } > > }; > > Ya, I like this too and would do it this way in most cases. However what about the case where you want one message on cancel if it happens during the first part of your task, but another message later in the task? Like "Cancelled while connecting" vs. "Cancelled while downloading data". In such a case you would need to have some thread safe state to communicate what you are doing so that from the cancelled method you can post the right message (note that cancelled() is called on the FX App thread, which is why it is safe to call updateMessage from there). The same can be said in the exception-throwing case: you need to keep some state somewhere. Ok it doesn't need to be threadsafe and can infact be a local variable instead of a field, but still. > > The cancelled() method would be called as soon as the task gets > > cancelled, where the application can perform the necessary cleanup, call > > updateProgress() or whatever without blowing up. > > > > Maybe this should be combined with throwing the TaskCancelledException > > (but only *after* the above call to cancelled() returned) to signal that > > the task can stop doing whatever it is doing, and roll up the executing > > thread. > > I see, so in this situation you would have cancelled be called on the background thread as opposed to the fx thread. I think I like it better when called on the FX thread so that only a single method, "call" is invoked on a background thread all others on the FX thread. This way developers know that only this one method will be called by the system on a background thread. I am a little undecided. I now come to think that since Task.cancel() is implemented by FutureTask.cancel() it should carry the same semantics. I.e. indicate that a task should not start running if it isn't started yet, and *maybe* interrupt a running task/thread (via a boolean parameter) in which case the implementer of the call() method must implement it in such a way that it handles interruption correctly/gracefully (e.g. check for Thread.isInterrupted() in a loop or handle InterruptedException when it does Thread.sleep(), etc). Which basically boils down to simply documenting all this. Hfrmp... I guess I need to go inside me a little more (it's too late around here..). Cheers, Roman From james.graham at oracle.com Thu Jan 5 15:49:12 2012 From: james.graham at oracle.com (Jim Graham) Date: Thu, 05 Jan 2012 15:49:12 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1325798506.7469.54.camel@moonlight> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> Message-ID: <4F0636F8.3020008@oracle.com> On 1/5/2012 1:21 PM, Roman Kennke wrote: > In my experience, when you call Thread.interrupt() on a thread that is > blocked in a network IO, it will not throw this exception. The only way > to 'cancel' such IO is to close() the stream, which will result in an > IOException in the blocked thread. I am not sure what this exception is > actually used for. Some stuff I found in the web seems to indicate it's > only used for PipedStreams: I vaguely recall that this had to be changed because the only reliable cross platform way to nudge a thread out of a socket read was to close the socket. Unfortunately, the method API contracts were all written such that the InterruptedIOException would return with the socket still open, so closing the socket wasn't a compatible option. So, they had to back off from Interrupt working on all socket streams and introduce a new stream which had the contract that it would get the IIOE and it would be closed as a result. But, back when the language was being modified to support interrupt() et al, the intention was that all IO operations would be interruptable in the same manner... ...jim From james.graham at oracle.com Thu Jan 5 15:52:58 2012 From: james.graham at oracle.com (Jim Graham) Date: Thu, 05 Jan 2012 15:52:58 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F061BC5.7030606@tbee.org> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> <4F061BC5.7030606@tbee.org> Message-ID: <4F0637DA.2080708@oracle.com> On 1/5/2012 1:53 PM, Tom Eugelink wrote: > I must admit that I've lost the discussion somewhere. > > But seeing the code above, with the if(isCancelled()) check to break the > loop, I do not see any need to throw an exception. The Task, after > returing from call(), can check itself for isCancelled() and call the > cancelled() method in the EDT (or whatever) for update. So, why an > exception? If they are updating the progress, then they are likely unaware that their actions are inappropriate (though it could have been a race condition in that they might have called isCancelled() and then they got cancelled after it returned "false" and then they call update() only to be in the cancel condition). It's roughly analogous to trying to read from a stream that has been closed. It's no longer appropriate to be reading from it and so you get an exceptional return. There is still the issue being discussed that in some cases it may be appropriate to update progress if the action has been canceled, though... ...jim From michael.heinrichs at oracle.com Fri Jan 6 01:01:41 2012 From: michael.heinrichs at oracle.com (Michael Heinrichs) Date: Fri, 6 Jan 2012 10:01:41 +0100 Subject: JavaBeanPropertyAdapter Reloaded In-Reply-To: References: <5FA5B819-93F8-4BC8-9123-04B2CF395B54@oracle.com> <10EFDBB3-AC59-44A1-AABE-0206DDAAC9ED@oracle.com> Message-ID: <32188125-6348-4B1E-9051-39765D37AA0F@oracle.com> Hi, are there any thought about the last point? I am really undecided how to continue. On the one hand I like the idea to use a familiar pattern for the JavaBeanPropertyBuilders, on the other hand I really hate replacing compile time checks with runtime checks. If nobody has an opinion, I will just throw a coin. :-) Thanks, Michael On 04.01.2012, at 10:12, Michael Heinrichs wrote: >>> public final class ReadOnlyJavaBeanPropertyBuilder { >>> public static ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >>> public static ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >>> // same for all other types >>> >>> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} >>> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName) throws NoSuchMethodException {...} >>> public ReadOnlyJavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter) {...} >>> >>> public ReadOnlyJavaBeanBooleanProperty createBooleanProperty(Object bean) {...} >>> public ReadOnlyJavaBeanObjectProperty createObjectProperty(Object bean) {...} >>> // same for all other types >>> } >>> >>> public final class JavaBeanPropertyBuilder { >>> public static JavaBeanBooleanProperty createBooleanProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >>> public static JavaBeanObjectProperty createObjectProperty(Object bean, String propertyName) throws NoSuchMethodException {...} >>> // same for all other types >>> >>> public JavaBeanPropertyBuilder(String propertyName, Class beanClass) throws NoSuchMethodException {...} >>> public JavaBeanPropertyBuilder(String propertyName, Class beanClass, String getterName, String setterName) throws NoSuchMethodException {...} >>> public JavaBeanPropertyBuilder(String propertyName, Class beanClass, Method getter, Method setter) {...} >>> >>> public JavaBeanBooleanProperty createBooleanProperty(Object bean) {...} >>> public JavaBeanObjectProperty createObjectProperty(Object bean) {...} >>> // same for all other types >>> } >> >> How do these fit with the existing builders that are generated? Do these use the same semantics / naming pattern / implement the same interface? >> > > No, right now they use a different pattern. I am undecided, if we should enforce the same pattern. A Java Bean adapter requires some mandatory parameters for which there are no default values. AFAIK there is no special treatment for such parameters in the builders, which means we cannot check during compile time, if all mandatory parameters were set. With the approach above on the other hand, you are forced to specify the mandatory parameters in the constructor / create()-method. > > JavaBeanBooleanPropertyBuilder.create().propertyName("x").bean(obj).build(); > If we would use the same pattern, we would only be able to check during runtime, if the propertyName was set for example. (Note that we would also need a builder for each type, which is probably not a bad idea anyway.) > > Do you think, we we should drop the compile time check to gain the more familiar pattern? > > >> Thanks >> Richard >>> >>> What do you think? >>> >>> Thanks, >>> Michael > From roman at kennke.org Fri Jan 6 02:47:03 2012 From: roman at kennke.org (Roman Kennke) Date: Fri, 06 Jan 2012 11:47:03 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1325803535.7469.73.camel@moonlight> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> <1325803535.7469.73.camel@moonlight> Message-ID: <1325846823.8979.7.camel@moonlight> Ok now, a little sleep does wonders ;-) Semantic-wise I think we do want something different than in FutureTask: basically we want to enable for a user to click on 'cancel' or similar which stops a long running operation. Those are our options so far: - A cancelled() callback method. Either we call it on the JFX (EDT) thread which would seem to be correct, but would potentially open a can of worms with synchronization/race conditions between this callback and the call() method. Or we call it on the background thread (how?) but that would seem unnatural and would actually be difficult (impossible) to implement. Worst of all, it does not necessarily stop the background task. - Throwing a TaskCancelledException in the background thread. We can only do that when the background thread is in one of the updateXXX() methods. If it's blocked in any other operation, we cannot do it (we cannot throw exceptions asynchronously). Moreover, we might want to do one last update on cancel. - Using thread interruption. This appears to be the only reasonable and safe way to implement it. My vote would be for cancel() to call FutureTask.cancel(true), document that the call() method should be implemented in a way that it handles thread interruption if the developer wants the task to be cancellable. For IO streams this involves using NIO InteruptibleChannels. I don't think we should throw IllegalStateException or TaskCancelledException anywhere. And the cancelled() callback seems to be difficult to implement without provocing race conditions. Regards, Roman Am Donnerstag, den 05.01.2012, 23:45 +0100 schrieb Roman Kennke: > Hi Richard, > > > > 2. How do we want to handle cancellation then: > > > > > >>> try { > > >>> doSomething(); > > >>> updateProgress(1, 4); > > >>> doSomething2(); > > >>> updateProgress(2, 4); > > >>> doSomething3(); > > >>> updateProgress(3, 4); > > >>> doSomething4(); > > >>> updateProgress(4, 4); > > >>> } catch (TaskCancelledException ex) { > > >>> // do stuff to clean up > > >>> // ... > > >>> > > >>> // Update the progress and message > > >>> Platform.runLater(() -> { > > >>> updateProgress(4, 4); > > >>> updateMessage("Cancelled"); > > >>> }); > > >>> } > > > > > > I think in general this is fine. But implementation-wise, how do you > > > make updateProgress() throw TaskCancelledException in the try block, > > > while not doing the same in the catch block? > > > > It is trivial. In the updateProgress method, I just do two checks: isCancelled() and a thread check. If the thread is not the FX App thread and isCancelled(), then throw an exception. In the above code I'm using Platform.runLater to run the updateProgress and updateMessage on the FX app thread, where it would still be legal. > > Uhm but it requires a specific pattern which is not obvious: use > runLater() in the catch handler and not use runLater() in the try block. > Good luck explaining that to developers! ;-) > > > > I like this approach (that you sent in your first email): > > > > > > new Task() { > > > public Object call() throws Exception { > > > for (int i=0; i<100; i++) { > > > if (isCancelled()) break; > > > try { > > > doSomething(); > > > updateProgress(i, 100); > > > } catch (Exception e) { } > > > } > > > return null; > > > } > > > > > > @Override protected void cancelled() { > > > updateMessage("Cancelled"); > > > } > > > }; > > > > Ya, I like this too and would do it this way in most cases. However what about the case where you want one message on cancel if it happens during the first part of your task, but another message later in the task? Like "Cancelled while connecting" vs. "Cancelled while downloading data". In such a case you would need to have some thread safe state to communicate what you are doing so that from the cancelled method you can post the right message (note that cancelled() is called on the FX App thread, which is why it is safe to call updateMessage from there). > > The same can be said in the exception-throwing case: you need to keep > some state somewhere. Ok it doesn't need to be threadsafe and can infact > be a local variable instead of a field, but still. > > > > The cancelled() method would be called as soon as the task gets > > > cancelled, where the application can perform the necessary cleanup, call > > > updateProgress() or whatever without blowing up. > > > > > > Maybe this should be combined with throwing the TaskCancelledException > > > (but only *after* the above call to cancelled() returned) to signal that > > > the task can stop doing whatever it is doing, and roll up the executing > > > thread. > > > > I see, so in this situation you would have cancelled be called on the background thread as opposed to the fx thread. I think I like it better when called on the FX thread so that only a single method, "call" is invoked on a background thread all others on the FX thread. This way developers know that only this one method will be called by the system on a background thread. > > I am a little undecided. I now come to think that since Task.cancel() is > implemented by FutureTask.cancel() it should carry the same semantics. > I.e. indicate that a task should not start running if it isn't started > yet, and *maybe* interrupt a running task/thread (via a boolean > parameter) in which case the implementer of the call() method must > implement it in such a way that it handles interruption > correctly/gracefully (e.g. check for Thread.isInterrupted() in a loop or > handle InterruptedException when it does Thread.sleep(), etc). Which > basically boils down to simply documenting all this. Hfrmp... I guess I > need to go inside me a little more (it's too late around here..). > > Cheers, Roman > > > From richard.bair at oracle.com Fri Jan 6 09:49:59 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 6 Jan 2012 09:49:59 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <1325846823.8979.7.camel@moonlight> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> <1325803535.7469.73.camel@moonlight> <1325846823.8979.7.camel@moonlight> Message-ID: > Ok now, a little sleep does wonders ;-) > > Semantic-wise I think we do want something different than in FutureTask: > basically we want to enable for a user to click on 'cancel' or similar > which stops a long running operation. Those are our options so far: > > - A cancelled() callback method. Either we call it on the JFX (EDT) > thread which would seem to be correct, but would potentially open a can > of worms with synchronization/race conditions between this callback and > the call() method. Or we call it on the background thread (how?) but > that would seem unnatural and would actually be difficult (impossible) > to implement. Worst of all, it does not necessarily stop the background > task. That's a good point, it really has to be called on the FX thread (which is right anyway I think). But this method does now exist in 2.1, along with other event handlers and methods for state transitions. That was done for other reasons than for this bug, but can have some usefulness for addressing this particular issue as well, if your needs are simple. > - Throwing a TaskCancelledException in the background thread. We can > only do that when the background thread is in one of the updateXXX() > methods. If it's blocked in any other operation, we cannot do it (we > cannot throw exceptions asynchronously). Moreover, we might want to do > one last update on cancel. > - Using thread interruption. This appears to be the only reasonable and > safe way to implement it. > > My vote would be for cancel() to call FutureTask.cancel(true) In fact, that is what I do already: @Override public boolean cancel(boolean mayInterruptIfRunning) { // Delegate to the super implementation to actually attempt to cancel this thing boolean flag = super.cancel(mayInterruptIfRunning); // If cancel succeeded (according to the semantics of the Future cancel method), // then we need to make sure the State flag is set appropriately if (flag) { // If this method was called on the FX application thread, then we can // just update the state directly and this will make sure that after // the cancel method was called, the state will be set correctly // (otherwise it would be indeterminate. However if the cancel method was // called off the FX app thread, then we must use runLater, and the // state flag will not be readable immediately after this call. However, // that would be the case anyway since these properties are not thread-safe. if (isFxApplicationThread()) { setState(State.CANCELLED); } else { runLater(new Runnable() { @Override public void run() { setState(State.CANCELLED); } }); } } // return the flag return flag; } > , document > that the call() method should be implemented in a way that it handles > thread interruption if the developer wants the task to be cancellable. > For IO streams this involves using NIO InteruptibleChannels. I don't > think we should throw IllegalStateException or TaskCancelledException > anywhere. And the cancelled() callback seems to be difficult to > implement without provocing race conditions. OK, so Roman votes for #2 -- just document. (#1 was throw the exception, #2 is just document to check for cancellation) I should also add, that in *any* case all documentation examples will show the use of isCancelled. If there are no strong opinions to throw the exception, then I'm inclined to take Roman's approach. Personally I think I would throw the exception, but I'm so on the fence on this one. Richard From tbee at tbee.org Fri Jan 6 11:22:02 2012 From: tbee at tbee.org (Tom Eugelink) Date: Fri, 06 Jan 2012 20:22:02 +0100 Subject: Review of solution for Cancelled Tasks In-Reply-To: References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> <1325803535.7469.73.camel@moonlight> <1325846823.8979.7.camel@moonlight> Message-ID: <4F0749DA.7090202@tbee.org> On 2012-01-06 18:49, Richard Bair wrote: > OK, so Roman votes for #2 -- just document. (#1 was throw the exception, #2 is just document to check for cancellation) FWIW: I was already on #2; this is not a JavaFX issue but a Java one, any fix should be a Java fix. Tom From hang.vo at oracle.com Fri Jan 6 12:12:19 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 06 Jan 2012 20:12:19 +0000 Subject: hg: openjfx/2.1/master/rt: 3 new changesets Message-ID: <20120106201220.5AE01478D5@hg.openjdk.java.net> Changeset: 3f64ddfadb20 Author: rbair Date: 2012-01-06 12:08 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3f64ddfadb20 Minor update to Task documentation ! javafx-concurrent/src/javafx/concurrent/Task.java Changeset: 21ffc59bcb2c Author: rbair Date: 2012-01-06 12:08 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/21ffc59bcb2c Very minor update to Task docs ! javafx-concurrent/src/javafx/concurrent/Service.java ! javafx-concurrent/src/javafx/concurrent/Worker.java Changeset: 051d56ac60ad Author: rbair Date: 2012-01-06 12:09 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/051d56ac60ad Documentation fixes as part of RT-17932. I added a large number of examples to the documentation to demonstrate best practices and how to handle cancellation from within a Task ! javafx-concurrent/src/javafx/concurrent/Task.java From richard.bair at oracle.com Fri Jan 6 12:35:34 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 6 Jan 2012 12:35:34 -0800 Subject: Review of solution for Cancelled Tasks In-Reply-To: <4F0749DA.7090202@tbee.org> References: <5826661A-A040-4061-825D-C41430CE34D0@oracle.com> <1325682673.3443.6.camel@moonlight> <4F04751C.5060102@oracle.com> <8B1FF0E4-59EE-46BE-A96C-8C4143B0A6C1@oracle.com> <4F04A1A1.6050503@oracle.com> <14E454D5-8B5F-46C6-B04A-FD043F60CE67@oracle.com> <4F04A314.1010406@oracle.com> <4F04A758.8090404@oracle.com> <6816AA28-61F8-4EEE-9731-29EBA7522104@reportmill.com> <4F04FC5C.6060308@oracle.com> <4F054160.7040309@tbee.org> <1325795365.7469.24.camel@moonlight> <19B5FC06-BF16-4663-9440-2337641F0CE7@oracle.com> <1325798506.7469.54.camel@moonlight> <1325803535.7469.73.camel@moonlight> <1325846823.8979.7.camel@moonlight> <4F0749DA.7090202@tbee.org> Message-ID: Thanks everyone, I've pushed the fixes to the documentation and the bug, you can see the issue for the changeset numbers. Richard On Jan 6, 2012, at 11:22 AM, Tom Eugelink wrote: > > On 2012-01-06 18:49, Richard Bair wrote: >> OK, so Roman votes for #2 -- just document. (#1 was throw the exception, #2 is just document to check for cancellation) > > FWIW: I was already on #2; this is not a JavaFX issue but a Java one, any fix should be a Java fix. > > Tom > > From hang.vo at oracle.com Fri Jan 6 12:42:10 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 06 Jan 2012 20:42:10 +0000 Subject: hg: openjfx/2.1/master/rt: Fixed RT-17932: Modified the Task so that the return value from "call" is ignored if the Task had been cancelled. Although a developer may updateMessage, updateText, or updateProgress from a cancelled task, we will ignore the return value. This effectively resolves RT-17932. I also update the documentation with an appropriate example of how to create a daemon thread to run a task. Message-ID: <20120106204210.61343478D6@hg.openjdk.java.net> Changeset: b138ee292e7b Author: rbair Date: 2012-01-06 12:34 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b138ee292e7b Fixed RT-17932: Modified the Task so that the return value from "call" is ignored if the Task had been cancelled. Although a developer may updateMessage, updateText, or updateProgress from a cancelled task, we will ignore the return value. This effectively resolves RT-17932. I also update the documentation with an appropriate example of how to create a daemon thread to run a task. ! javafx-concurrent/src/javafx/concurrent/Task.java ! javafx-concurrent/test/javafx/concurrent/TaskCancelTest.java From richard.bair at oracle.com Fri Jan 6 12:51:01 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 6 Jan 2012 12:51:01 -0800 Subject: Incremental updates from Tasks Message-ID: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> Hi, OK, here is the next issue I ran into while documenting the Task for the last issue. We don't really have a good way for the Task to do incremental updates to the Task value. Instead, it is only set at the conclusion of the call method, and only when the Task isn't cancelled. This is fairly limiting. Instead, it would be nice to allow Task subclasses to set the value whenever they like -- before execution (such as in the constructor), multiple times during execution, or even after it has been cancelled. This is the issue: http://javafx-jira.kenai.com/browse/RT-18820 There are two parts to this solution. The first is solving incremental updates when the value is an atomic value (String, int, Customer, etc). The second is solving incremental updates for collections. I will tackle these separately (I think the best answer is to have a simple updateValue for everything and a special ObservableListTask which handles Tasks that return an ObservableList with all the fancy bells and whistles for dealing with incremental updates to an ObservableList). Here is my proposed new API to add to Task: /** * Updates the value property with the supplied value. This * can be done at any time. A final updateValue is called * automatically by Task at the completion of the call * method. This method allows Task implementations to provide partial * results. * *

For example, you may have a Task which computes prime numbers. Each * prime number computed could become the new value of the * Task. Simply invoke updateValue passing the most recently computed * prime number.

* *

As with the other update methods, Calls to updateValue * are coalesced and run later on the FX application thread, so calls * to updateValue, even from the FX Application thread, may not * necessarily result in immediate updates to this property, and * intermediate values may be coalesced to save on event * notifications. *

* This method is safe to be called from any thread. *

* * @param value The new value for the value property of Task. */ protected void updateValue(V value) { ... } I'm formulating the fix for the ObservableListTask (I figure that is what I will call it, although it isn't all that nice a name to be honest, but it is right to the point). Richard From roman at kennke.org Fri Jan 6 13:00:54 2012 From: roman at kennke.org (Roman Kennke) Date: Fri, 06 Jan 2012 22:00:54 +0100 Subject: Incremental updates from Tasks In-Reply-To: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> Message-ID: <1325883654.3075.5.camel@moonlight> Hi Richard, > OK, here is the next issue I ran into while documenting the Task for the last issue. We don't really have a good way for the Task to do incremental updates to the Task value. Instead, it is only set at the conclusion of the call method, and only when the Task isn't cancelled. This is fairly limiting. Instead, it would be nice to allow Task subclasses to set the value whenever they like -- before execution (such as in the constructor), multiple times during execution, or even after it has been cancelled. > > This is the issue: http://javafx-jira.kenai.com/browse/RT-18820 I was thinking about the same when you asked about how to get the state (thread safe) into a cancelled() callback method. An API to do partial updates would be helpful. What about an API similar to how it's done in SwingWorker? There we define a 2nd type parameter T and publish() partial results. This would be called from the call() method whenever we have a partial result: protected void publish(T chunk) Which in turn results into a call (on the JavaFX thread) to: protected void process(List chunks...) Which allows to process one or more chunks that have been published before using publish(). I like that API and it solves the question how to handle atomic vs. list types simply by introducing a new generic type. It also has the advantage that Swing refugees feel familiar with it :-) Anything wrong with that approach? Regards, Roman From richard.bair at oracle.com Fri Jan 6 13:10:26 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 6 Jan 2012 13:10:26 -0800 Subject: Incremental updates from Tasks In-Reply-To: <1325883654.3075.5.camel@moonlight> References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> <1325883654.3075.5.camel@moonlight> Message-ID: <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> Hi Roman, >> OK, here is the next issue I ran into while documenting the Task for the last issue. We don't really have a good way for the Task to do incremental updates to the Task value. Instead, it is only set at the conclusion of the call method, and only when the Task isn't cancelled. This is fairly limiting. Instead, it would be nice to allow Task subclasses to set the value whenever they like -- before execution (such as in the constructor), multiple times during execution, or even after it has been cancelled. >> >> This is the issue: http://javafx-jira.kenai.com/browse/RT-18820 > > I was thinking about the same when you asked about how to get the state > (thread safe) into a cancelled() callback method. An API to do partial > updates would be helpful. > > What about an API similar to how it's done in SwingWorker? There we > define a 2nd type parameter T and publish() partial results. This would > be called from the call() method whenever we have a partial result: > > protected void publish(T chunk) > > Which in turn results into a call (on the JavaFX thread) to: > > protected void process(List chunks...) > > Which allows to process one or more chunks that have been published > before using publish(). > > I like that API and it solves the question how to handle atomic vs. list > types simply by introducing a new generic type. It also has the > advantage that Swing refugees feel familiar with it :-) > > Anything wrong with that approach? There are two things I don't like about that approach. The first is that it introduces overhead in cases where we are only interested in the "most recent intermediate value". That "chunks" list might be 10,000 elements long, and we only care about the very last item in it. The second thing I don't like about it is that for cases where you are computing an atomic value, it is more complicated. You have to call publish and implement process. With the proposed updateValue, you simply call updateValue from your Task implementation and the rest is just handled for you. The Swing solution here I think is quite flexible -- and that is both its strength and weakness. Although this might very well be what I should use for the observable list case. I guess it comes down to a trade-off. Do we have a single class that handles incremental updates to atomic & collection values (reduced number of classes, less "cluttered" namespace, worse performance for updates to atomic values that are frequently updated), or do we split this up into multiple classes (more classes, better performance, more situation-specific API)? I do remember feeling that publish / process was awkward to use back when I was learning it, and I didn't spend enough years with it for it to become natural I guess :-) Richard From roman at kennke.org Fri Jan 6 13:28:24 2012 From: roman at kennke.org (Roman Kennke) Date: Fri, 06 Jan 2012 22:28:24 +0100 Subject: Incremental updates from Tasks In-Reply-To: <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> <1325883654.3075.5.camel@moonlight> <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> Message-ID: <1325885304.3075.26.camel@moonlight> Hi Richard, > >> OK, here is the next issue I ran into while documenting the Task for the last issue. We don't really have a good way for the Task to do incremental updates to the Task value. Instead, it is only set at the conclusion of the call method, and only when the Task isn't cancelled. This is fairly limiting. Instead, it would be nice to allow Task subclasses to set the value whenever they like -- before execution (such as in the constructor), multiple times during execution, or even after it has been cancelled. > >> > >> This is the issue: http://javafx-jira.kenai.com/browse/RT-18820 > > > > I was thinking about the same when you asked about how to get the state > > (thread safe) into a cancelled() callback method. An API to do partial > > updates would be helpful. > > > > What about an API similar to how it's done in SwingWorker? There we > > define a 2nd type parameter T and publish() partial results. This would > > be called from the call() method whenever we have a partial result: > > > > protected void publish(T chunk) > > > > Which in turn results into a call (on the JavaFX thread) to: > > > > protected void process(List chunks...) > > > > Which allows to process one or more chunks that have been published > > before using publish(). > > > > I like that API and it solves the question how to handle atomic vs. list > > types simply by introducing a new generic type. It also has the > > advantage that Swing refugees feel familiar with it :-) > > > > Anything wrong with that approach? > > There are two things I don't like about that approach. The first is that it introduces overhead in cases where we are only interested in the "most recent intermediate value". That "chunks" list might be 10,000 elements long, and we only care about the very last item in it. The chunks list is usually short. Whenever the background thread calls publish() the item is put into the chunks list, as soon as the EDT gets to run (hopefully often enough!) whatever is in that list gets process()ed. The next publish() starts in a fresh list. In other words, we only get the latest bunch of data. Whether the implementation keeps all the data around or not is up to itself. > The second thing I don't like about it is that for cases where you are computing an atomic value, it is more complicated. You have to call publish and implement process. With the proposed updateValue, you simply call updateValue from your Task implementation and the rest is just handled for you. Hmm yes. If we do a generic Task class and specific subclasses for lists or atomics, the atomic version could by default have the same V and T and call updateValue() in its implementation of process() (implementation could optionally override it to do its own processing). > The Swing solution here I think is quite flexible -- and that is both its strength and weakness. Although this might very well be what I should use for the observable list case. > > I guess it comes down to a trade-off. Do we have a single class that handles incremental updates to atomic & collection values (reduced number of classes, less "cluttered" namespace, worse performance for updates to atomic values that are frequently updated), or do we split this up into multiple classes (more classes, better performance, more situation-specific API)? > > I do remember feeling that publish / process was awkward to use back when I was learning it, and I didn't spend enough years with it for it to become natural I guess :-) I'd think that it would be good to have a generic and superflexible class (maybe based on publish/process like API) and at least 2 specific subclasses, for atomic and for lists. I already outlined how an atomic version could work above. A list version would have List as V and T for partial values, and maybe also do some default stuff in process() (like updateProgress() ). Roman From richard.bair at oracle.com Fri Jan 6 14:54:48 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 6 Jan 2012 14:54:48 -0800 Subject: Incremental updates from Tasks In-Reply-To: <1325885304.3075.26.camel@moonlight> References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> <1325883654.3075.5.camel@moonlight> <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> <1325885304.3075.26.camel@moonlight> Message-ID: <72CB0F3D-2D1C-442E-B65F-20EF73E7D8C9@oracle.com> >>> What about an API similar to how it's done in SwingWorker? There we >>> define a 2nd type parameter T and publish() partial results. This would >>> be called from the call() method whenever we have a partial result: >>> >>> protected void publish(T chunk) >>> >>> Which in turn results into a call (on the JavaFX thread) to: >>> >>> protected void process(List chunks...) >>> >>> Which allows to process one or more chunks that have been published >>> before using publish(). >>> >>> I like that API and it solves the question how to handle atomic vs. list >>> types simply by introducing a new generic type. It also has the >>> advantage that Swing refugees feel familiar with it :-) >>> >>> Anything wrong with that approach? >> >> There are two things I don't like about that approach. The first is that it introduces overhead in cases where we are only interested in the "most recent intermediate value". That "chunks" list might be 10,000 elements long, and we only care about the very last item in it. > > The chunks list is usually short. Whenever the background thread calls > publish() the item is put into the chunks list, as soon as the EDT gets > to run (hopefully often enough!) whatever is in that list gets > process()ed. The next publish() starts in a fresh list. In other words, > we only get the latest bunch of data. Whether the implementation keeps > all the data around or not is up to itself. I think in the case of something like this: Task task = new Task() { @Override protected Integer call() throws Exception { int iterations; for (iterations = 0; iterations < 10000000; iterations++) { if (isCancelled()) { break; } updateMessage("Iteration " + iterations); updateProgress(iterations, 10000000); updateValue(iterations); } return iterations; } }; We would see very many items added to the chunk list. I wonder what are some good practical use cases? I suggested computing primes, or computing pi, or some other such algorithm. In those cases I suspect we will flood the event queue very quickly (remember we only run pulses at 60 times per second). I ran an interesting test case. I had an AtomicInteger to act as my counter, and found an implementation of computing primes in Java. I wrote a task and tracked how often updateValue was called vs. how often the value property was updated on the FX thread. Results (some intermediate set of counts): 7 2 6 454 27 5 4 5 4 3 6 310 3 124 So sometimes it kept up well, other times it was somewhat slower. I would guess the assertion that "there aren't that many chunks" is probably generally accurate, but not always. Interestingly, the very first counts I got were: 4178 62 23 26 26 Indicating that before things got hot-spotted there was a lot more data being buffered up. In any case, with mobile and embedded devices, I'm worried about having a queue that large that I'm populating and dumping with regularity (garbage collection has a big impact on battery life, so I'm told). I need to do more experimentation. The ObservableListTask is actually kind of a mess. The problem is that the call() method returns the final result, but in an ObservableListTask you don't want to force the implementation of ObservableListTask to return a result! In fact, for such a Task you want the result set immediately in the constructor and then never set again -- only have items added to it. It almost suggests that ObservableListTask should extend from FutureTask and not from Task, duplicating a chunk of the Task API. Or that there should be a TaskBase extending from FutureTask with Task and ObservableListTask extending from it (I guess that is along the lines of what you were suggesting, isn't it??). Richard From hang.vo at oracle.com Fri Jan 6 15:12:10 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 06 Jan 2012 23:12:10 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18805: SplitPane divider issues. Message-ID: <20120106231210.CB2AF478D7@hg.openjdk.java.net> Changeset: e3debba6759b Author: Kinsley Wong Date: 2012-01-06 15:06 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e3debba6759b RT-18805: SplitPane divider issues. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/SplitPaneSkin.java ! javafx-ui-controls/test/javafx/scene/control/SplitPaneTest.java From richard.bair at oracle.com Fri Jan 6 15:40:10 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 6 Jan 2012 15:40:10 -0800 Subject: Incremental updates from Tasks In-Reply-To: <72CB0F3D-2D1C-442E-B65F-20EF73E7D8C9@oracle.com> References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> <1325883654.3075.5.camel@moonlight> <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> <1325885304.3075.26.camel@moonlight> <72CB0F3D-2D1C-442E-B65F-20EF73E7D8C9@oracle.com> Message-ID: <470804F4-4D60-487A-85A9-8472F939D230@oracle.com> I tried the following. I created a TaskBase, and moved almost everything from Task to TaskBase. Task now extends TaskBase, and adds: updateValue(V value); abstract V call() throws Exception; It is entirely compatible with the previous version and all the unit tests still pass. The only difference is that the method we expose (call) returns a value and is declared on Task instead of TaskBase. Also, the updateValue is only on Task. ObservableListTask also extends TaskBase, and adds: protected abstract void call() throws Exception; protected void publish(E... items); protected void publish(Collection items); I just used the SwingWorker name "publish", but there is no process. Whatever you publish gets added. We could add a process with a default implementation if we wanted to (allowing some users to filter results or whatnot, but I'm not sure there is a reason you want to do that on the FX thread instead of the background thread, so I would propose leaving it off for now). This call() method returns no value, just the way it should be. You just call publish to put add items. Since the Task is a one-shot deal, there isn't really a compelling reason to add other methods like clear() (you could also do your own runLater() if you needed to do something tricky). What do you think? Here is an example of usage (complete with fading rectangles :-)): ObservableListTask task = new ObservableListTask() { @Override protected void call() throws Exception { for (int i=0; i<900; i++) { Rectangle r = new Rectangle(10, 10); r.setFill(Color.color(Math.random(), Math.random(), Math.random())); r.setOpacity(0); FadeTransition tx = new FadeTransition(Duration.seconds(1)); tx.setToValue(1.0); tx.setNode(r); tx.play(); publish(r); Thread.sleep(20); updateProgress(i, 900); } } }; Richard From richard.bair at oracle.com Fri Jan 6 15:47:01 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 6 Jan 2012 15:47:01 -0800 Subject: Review: ScheduledService Message-ID: <84AB2BC0-0956-4881-8A85-2BB0D40FD064@oracle.com> Here's another one that needs review. The patch supplied with the issue is pretty detailed (its the whole implementation + documentation). The big thing to look for here is going to be semantic incompatibilities with Service (I don't think there are any) or any weirdness in the API. There are some pretty cool features on this class, I think. http://javafx-jira.kenai.com/browse/RT-18702 Thanks Richard From alexandre.iline at oracle.com Mon Jan 9 08:29:30 2012 From: alexandre.iline at oracle.com (Alexandre (Shura) Iline) Date: Mon, 09 Jan 2012 08:29:30 -0800 Subject: Testing keyboard events in controls In-Reply-To: <1325679053.3443.2.camel@moonlight> References: <4F03EBA6.1080508@oracle.com> <1325679053.3443.2.camel@moonlight> Message-ID: <4F0B15EA.9020207@oracle.com> On 01/04/2012 04:10 AM, Roman Kennke wrote: > Hi Jonathan, > > Happy New Year to you and the whole JFX team! > > Not having looked at your code yet, I am wondering if it would make > sense to add a (java.awt.)Robot-like class to JavaFX? Maybe the AWT > Robot can even be used directly? (after all, it generates OS level > events, I believe it doesn't matter if it's an AWT window, JavaFX window > or any other window). This is what currently used in JemmyFX - AWT robot. In a nearest future I am going to open source the whole thing. We are testing JavaFX itself (and apps based on it) with JemmyFX for quite some time, as you could imagine. So far I have not seen anything which AWT robot can not do on the platforms we test on. Should, for some reason, we have a JavaFX robot, it would be easy to plug it into JemmyFX tests with no change to test code itself - only changing the test environment. Shura. > > A while ago I was thinking of building a FEST (for Swing) like testing > framework for JavaFX (for the ThingsFX project), and I'll certainly > start this once I have some time again. This should also be useful for > testing inside JavaFX itself. > > Cheers, Roman > > Am Mittwoch, den 04.01.2012, 16:03 +1000 schrieb Jonathan Giles: >> Hi all (and happy new year!), >> >> ListView/TreeView/TableView have a heap of keyboard requirements that >> come in from UX. Keyboard event handling code is often fraught with >> complexity due to the many permutations. I'm frequently getting burnt >> due to subtle changes in this code causing unintended regressions. >> >> I finally decided to do something about this, and have thrown together a >> very simple set of APIs to make it possible to write simple unit tests >> that test keyboard navigation. It's all very lightweight so that it can >> easily run as part of the JavaFX engineering continuous build (which >> means it fails sooner, compared to SQE tests, or even worse - when it >> ends up in the hands of developers!). Additionally, it's all very >> primitive and proof-of-concept at this stage, and I am happy to refine >> (and relocate) the class to provide utility in this area if it doesn't >> duplicate existing functionality I'm unaware of. I'm also certain I'm >> missing some of the details on how best to do this, so feedback is welcome. >> >> Anywho, you can find the class in the rt/javafx-ui-controls project, in >> the test directory, in javafx.scene.control.KeyEventFirer. You can see a >> few unit tests that were used to flesh out the API in >> javafx.scene.control.ListViewKeyInputTest. I plan to add many more here, >> and for other controls, in the coming weeks and months. >> >> These are the key pointers: >> 1) When creating a KeyEventFirer, you must provide an EventTarget. >> Despite the scary name, all Nodes are EventTargets. >> 2) I put the ListView inside a group, which is placed in a scene, which >> itself is placed in a stage. I show() and hide() the stage as necessary >> (in the setup() / tearDown() methods). Without this, events don't fire. >> 3) I use the separate javafx.scene.control.KeyModifier class to provide >> zero or more keyboard modifiers (shift, alt, ctrl, meta) to the keyboard >> input. An enum might already exist for these modifiers, but I'm not sure... >> 4) I have convenience methods (for my needs, anyway) for >> up/down/left/right keyboard inputs, in the form of >> 'do*ArrowPress(KeyModifier... modifiers)' methods. I'm sure more can be >> added... >> 5) For all other keyboard input, you can use the 'doKeyPress(KeyCode >> keyCode, KeyModifier... modifiers)' method. >> >> Any feedback would be appreciated. >> > > From alexandre.iline at oracle.com Mon Jan 9 08:41:41 2012 From: alexandre.iline at oracle.com (Alexandre (Shura) Iline) Date: Mon, 09 Jan 2012 08:41:41 -0800 Subject: Testing keyboard events in controls In-Reply-To: <4F03EBA6.1080508@oracle.com> References: <4F03EBA6.1080508@oracle.com> Message-ID: <4F0B18C5.5080801@oracle.com> Jonathan, What you are suggesting seem to be good for unit test of controls code. You could create a unit test where you send an event and verify that the component is reacting appropriately. In case of functional testing of applications build with JavaFX (or a bigger scale unit testing, for this matter), what you really have to do is to click with mouse. A control could have any number of listeners and so, when testing "functionally" you need simulate user actions as close as possible. We right now click with AWT robot, but could use any other robot, like I am saying in the other e-mail. It is all possible and relatively simple to do with JemmyFX right now (for years, I should say). Yes, it is a little bit more complicated than doRightArrowPress() (it would be something like pressKey(KeyBoardButtons.RIGHT)), but not that complicated altogether. If you want I will stop by your office and show you how to so that. Shura PS: Like it is mentioned in another e-mail, JemmyFX is planned to be opensourced _really_ soon. On 01/03/2012 10:03 PM, Jonathan Giles wrote: > Hi all (and happy new year!), > > ListView/TreeView/TableView have a heap of keyboard requirements that > come in from UX. Keyboard event handling code is often fraught with > complexity due to the many permutations. I'm frequently getting burnt > due to subtle changes in this code causing unintended regressions. > > I finally decided to do something about this, and have thrown together a > very simple set of APIs to make it possible to write simple unit tests > that test keyboard navigation. It's all very lightweight so that it can > easily run as part of the JavaFX engineering continuous build (which > means it fails sooner, compared to SQE tests, or even worse - when it > ends up in the hands of developers!). Additionally, it's all very > primitive and proof-of-concept at this stage, and I am happy to refine > (and relocate) the class to provide utility in this area if it doesn't > duplicate existing functionality I'm unaware of. I'm also certain I'm > missing some of the details on how best to do this, so feedback is welcome. > > Anywho, you can find the class in the rt/javafx-ui-controls project, in > the test directory, in javafx.scene.control.KeyEventFirer. You can see a > few unit tests that were used to flesh out the API in > javafx.scene.control.ListViewKeyInputTest. I plan to add many more here, > and for other controls, in the coming weeks and months. > > These are the key pointers: > 1) When creating a KeyEventFirer, you must provide an EventTarget. > Despite the scary name, all Nodes are EventTargets. > 2) I put the ListView inside a group, which is placed in a scene, which > itself is placed in a stage. I show() and hide() the stage as necessary > (in the setup() / tearDown() methods). Without this, events don't fire. > 3) I use the separate javafx.scene.control.KeyModifier class to provide > zero or more keyboard modifiers (shift, alt, ctrl, meta) to the keyboard > input. An enum might already exist for these modifiers, but I'm not sure... > 4) I have convenience methods (for my needs, anyway) for > up/down/left/right keyboard inputs, in the form of > 'do*ArrowPress(KeyModifier... modifiers)' methods. I'm sure more can be > added... > 5) For all other keyboard input, you can use the 'doKeyPress(KeyCode > keyCode, KeyModifier... modifiers)' method. > > Any feedback would be appreciated. > From parvathi.somashekar at oracle.com Mon Jan 9 10:30:00 2012 From: parvathi.somashekar at oracle.com (Paru Somashekar) Date: Mon, 09 Jan 2012 10:30:00 -0800 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: <4EE94B5E.8080401@oracle.com> References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> Message-ID: <4F0B3228.9050204@oracle.com> Hello All, The following is the proposal (follow up from previous email by Jonathan) from UI Controls team towards an API to support native Mac OS menubar. 1) A new property useGlobalMenuBar would be added to MenuBar class whose initial value will be set to a default dictated by a property settable via CSS ( and set to false by default). Once again, for the first cut of this support in this release, this will be the only public API for native menubar support. 2) In the case when we have multiple Menubars specified on a stage, the first one wins and will be made global, i.e. provided the platform supports native integration and useGlobalMenuBar is set to true. 3) As mentioned in a previous email by Jonathan, we will have hooks to switch between stages when they become active and swap menu bars. In the case when we don?t find a Menubar, it will be nulled out. This is the path we are choosing for now and we realize that we can change this later with no backward compatibility issues. We are extending the same concept to the scenario where, when the last window closes; it will be nulled out as well and hence there is no concept of a default menubar. thanks, Paru. On 12/14/11 5:20 PM, Kevin Rushforth wrote: > An API on Stage to set a menu bar has a certain elegance, plus it gets > around the backward compatibility and inconsistency problems that > having "native" on by default would create. Unfortunately, as Rich > pointed out earlier, it creates an unwanted dependency from a > modularity point of view since Stage will be in the base module and > should not depend on anything in controls. > > -- Kevin > > > steve.x.northover at oracle.com wrote: >> Hello all, >> >> How about an API in Stage where you set the menu bar for the stage? >> This is the menu that you wish to be native and the application very >> is clear about this. There can only be one real menu bar on Windows. >> Application code can ask for the menu bar and add items to it. If you >> have a property, application code needs to do the same search that FX >> does to determine if there is an active menu bar. >> >> Here is what I think about focus: On the Mac, when a stage gets >> focus, it sets the native menu bar. When it loses focus, it clears >> the native menu bar. Retaining the menu from the previous stage is >> unlikely to be the right thing to do as an application can have many >> different stages around and only the application can know whether the >> current menu bar applies to a stage. >> >> Steve >> >> On 14/12/2011 6:31 PM, Jonathan Giles wrote: >>> Hi All, >>> >>> Here's an update from the UI controls team as to how we see the >>> native Mac OS menubar support working. Your thoughts are appreciated. >>> >>> After discussing it again today, we think that the approach >>> suggested by Richard in an earlier email in this thread makes the >>> best sense, in terms of modularity and code cleanliness. I'll >>> explain this further shortly... >>> >>> The thinking is to add a new property to >>> javafx.scene.control.MenuBar. We haven't settled on a name, but it's >>> something along the lines of 'native', 'global', 'globalMenuBar', >>> 'screenMenuBar', or 'applicationMenuBar'. Whatever property name we >>> use, we'll expand it out to have the usual set*/get*/*property >>> methods. This would be the only public API we end up adding for >>> native menubar support. For the remainder of this email I refer to >>> this property as 'native'. >>> >>> This property will by default be true, indicating that on platforms >>> where we support native integration, it'll happen by default. >>> >>> On a platform that supports native integration, we'll find the >>> 'first' MenuBar in the scene that has the 'native' property set to >>> true. We can't guarantee that we'll find necessarily the physically >>> top-most MenuBar as that is really a matter of how the scenegraph is >>> laid out. Of course, this is only a problem in situations where the >>> scene contains multiple MenuBars where 'native' is true in more than >>> one of them, which we hope won't often be the case. If a Scene does >>> have multiple MenuBars with 'native' set to true, the behaviour is >>> undefined. If the wrong MenuBar is made native, you can help provide >>> a hint by setting 'native' to false in the relevant place(s). >>> >>> We'll also hook into the Stage and listen to the relevant events, >>> such that when a Stage gains focus, we'll switch in any native >>> menubars found in the scene of that stage. If no relevant MenuBar is >>> found, then we can either retain the MenuBar from the previous >>> stage, or null it out. I'm going to assume the former is by far >>> going to win this vote, but feel free to surprise me. >>> >>> Using this approach, developer code should be cleaner. Your user >>> interface should position a MenuBar where it makes sense for your >>> application, regardless of the operating system (normally at the >>> very top of your scene). On platforms where native integration is >>> supported, the JavaFX-rendered MenuBar will not be rendered >>> (although it'll likely remain in the scenegraph as a no-op control). >>> If the 'native' property changes, we'll flick between the native and >>> JavaFX-rendered MenuBar as expected. This approach means there is no >>> operating system dependent code in your user interface. >>> >>> As I mentioned - we're totally open to discussion on any of these >>> points. Any thoughts? >>> >>> -- Jonathan >>> >>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: >>>> Hi all, >>>> >>>> One of the things we're planning to support in JavaFX 2.1 is the >>>> native Mac OS menubar. This email is intended primarily to discuss >>>> the API one expects to see to set a MenuBar in the native Mac OS >>>> menubar area. Your feedback is sought and will be very much >>>> appreciated. >>>> >>>> The current thinking is that Application feels like the right place >>>> to specify a global, application-wide javafx.scene.control.MenuBar >>>> on. It could be assumed that if a developer were to set this >>>> property, and the operating system upon which the end-user was >>>> running the JavaFX application was Mac OS, that the menubar will be >>>> displayed using the native Mac OS menubar. Of course, if a >>>> developer wants a cross-platform look and feel, they could just >>>> place the MenuBar in the stage as per usual and it would display as >>>> it currently does. This approach opens up a number of questions and >>>> issues: >>>> >>>> 1) What happens in the case of the end-user being on Windows? Is >>>> the Application.MenuBar ignored, or is it automagically added to >>>> the main Stage? (I would argue for totally ignoring it....but that >>>> leads to the next point). >>>> 2) This approach means there needs to be operating specific code in >>>> the UI to test whether a non-native MenuBar should be added (in the >>>> case of Windows, for example). This starts to clutter the UI code, >>>> and without careful consideration by the developer may result in >>>> needing to duplicate their MenuBar code. Is there a better approach? >>>> >>>> Another place to specify a MenuBar would be on Stage, rather than >>>> (or in addition to), Application. Having a MenuBar property on >>>> Stage would allow for the MenuBar to change based on the currently >>>> focused Stage - but I'm not certain this is desirable or even the >>>> expected behaviour of Mac OS. Therefore, I'm thinking that this is >>>> not likely to happen unless we hear otherwise. >>>> >>>> Like I said, we're at a very early exploration point in this >>>> process. The controls team is very keen to hear feedback from the >>>> community, as well as from the owners of the Application API, and >>>> the Mac OS experts on this list. >>>> >>>> Thanks, >>>> -- Jonathan From steve.x.northover at oracle.com Mon Jan 9 10:45:30 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Mon, 09 Jan 2012 13:45:30 -0500 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: <4F0B3228.9050204@oracle.com> References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> <4F0B3228.9050204@oracle.com> Message-ID: <4F0B35CA.7070008@oracle.com> Hello! I believe there are problems with 1) and 2). First off, there is no way to mix and match. For example, suppose I want some shells to use the global menu bar and others to use a local menu bar. Further, application code may put trimmings around a menu bar and when the menu bar is "hoisted up", the window might look strange. If we are doing 3), I think a better approach would be for the application to decide whether it is using native menu bars or not by explicitly setting a menu bar on a stage. It makes sense because on platforms like Windows and GTK, there is a one-to-one mapping between stage and menu bar so why not make this explicit in the API? One of the problems with having API in Stage is that we don't want a reference to MenuBar. Why don't we define something like Stage.setMenyBarArea() or something like that to take a Node or another type instead? Steve On 09/01/2012 1:30 PM, Paru Somashekar wrote: > Hello All, > > The following is the proposal (follow up from previous email by > Jonathan) from UI Controls team towards an API to support native Mac > OS menubar. > > 1) A new property useGlobalMenuBar would be added to MenuBar class > whose initial value will be set to a default dictated by a property > settable via CSS ( and set to false by default). Once again, for the > first cut of this support in this release, this will be the only > public API for native menubar support. > > 2) In the case when we have multiple Menubars specified on a stage, > the first one wins and will be made global, i.e. provided the platform > supports native integration and useGlobalMenuBar is set to true. > > 3) As mentioned in a previous email by Jonathan, we will have hooks to > switch between stages when they become active and swap menu bars. In > the case when we don?t find a Menubar, it will be nulled out. This is > the path we are choosing for now and we realize that we can change > this later with no backward compatibility issues. > We are extending the same concept to the scenario where, when the last > window closes; it will be nulled out as well and hence there is no > concept of a default menubar. > > thanks, > Paru. > > On 12/14/11 5:20 PM, Kevin Rushforth wrote: >> An API on Stage to set a menu bar has a certain elegance, plus it >> gets around the backward compatibility and inconsistency problems >> that having "native" on by default would create. Unfortunately, as >> Rich pointed out earlier, it creates an unwanted dependency from a >> modularity point of view since Stage will be in the base module and >> should not depend on anything in controls. >> >> -- Kevin >> >> >> steve.x.northover at oracle.com wrote: >>> Hello all, >>> >>> How about an API in Stage where you set the menu bar for the stage? >>> This is the menu that you wish to be native and the application very >>> is clear about this. There can only be one real menu bar on Windows. >>> Application code can ask for the menu bar and add items to it. If >>> you have a property, application code needs to do the same search >>> that FX does to determine if there is an active menu bar. >>> >>> Here is what I think about focus: On the Mac, when a stage gets >>> focus, it sets the native menu bar. When it loses focus, it clears >>> the native menu bar. Retaining the menu from the previous stage is >>> unlikely to be the right thing to do as an application can have many >>> different stages around and only the application can know whether >>> the current menu bar applies to a stage. >>> >>> Steve >>> >>> On 14/12/2011 6:31 PM, Jonathan Giles wrote: >>>> Hi All, >>>> >>>> Here's an update from the UI controls team as to how we see the >>>> native Mac OS menubar support working. Your thoughts are appreciated. >>>> >>>> After discussing it again today, we think that the approach >>>> suggested by Richard in an earlier email in this thread makes the >>>> best sense, in terms of modularity and code cleanliness. I'll >>>> explain this further shortly... >>>> >>>> The thinking is to add a new property to >>>> javafx.scene.control.MenuBar. We haven't settled on a name, but >>>> it's something along the lines of 'native', 'global', >>>> 'globalMenuBar', 'screenMenuBar', or 'applicationMenuBar'. Whatever >>>> property name we use, we'll expand it out to have the usual >>>> set*/get*/*property methods. This would be the only public API we >>>> end up adding for native menubar support. For the remainder of this >>>> email I refer to this property as 'native'. >>>> >>>> This property will by default be true, indicating that on platforms >>>> where we support native integration, it'll happen by default. >>>> >>>> On a platform that supports native integration, we'll find the >>>> 'first' MenuBar in the scene that has the 'native' property set to >>>> true. We can't guarantee that we'll find necessarily the physically >>>> top-most MenuBar as that is really a matter of how the scenegraph >>>> is laid out. Of course, this is only a problem in situations where >>>> the scene contains multiple MenuBars where 'native' is true in more >>>> than one of them, which we hope won't often be the case. If a Scene >>>> does have multiple MenuBars with 'native' set to true, the >>>> behaviour is undefined. If the wrong MenuBar is made native, you >>>> can help provide a hint by setting 'native' to false in the >>>> relevant place(s). >>>> >>>> We'll also hook into the Stage and listen to the relevant events, >>>> such that when a Stage gains focus, we'll switch in any native >>>> menubars found in the scene of that stage. If no relevant MenuBar >>>> is found, then we can either retain the MenuBar from the previous >>>> stage, or null it out. I'm going to assume the former is by far >>>> going to win this vote, but feel free to surprise me. >>>> >>>> Using this approach, developer code should be cleaner. Your user >>>> interface should position a MenuBar where it makes sense for your >>>> application, regardless of the operating system (normally at the >>>> very top of your scene). On platforms where native integration is >>>> supported, the JavaFX-rendered MenuBar will not be rendered >>>> (although it'll likely remain in the scenegraph as a no-op >>>> control). If the 'native' property changes, we'll flick between the >>>> native and JavaFX-rendered MenuBar as expected. This approach means >>>> there is no operating system dependent code in your user interface. >>>> >>>> As I mentioned - we're totally open to discussion on any of these >>>> points. Any thoughts? >>>> >>>> -- Jonathan >>>> >>>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: >>>>> Hi all, >>>>> >>>>> One of the things we're planning to support in JavaFX 2.1 is the >>>>> native Mac OS menubar. This email is intended primarily to discuss >>>>> the API one expects to see to set a MenuBar in the native Mac OS >>>>> menubar area. Your feedback is sought and will be very much >>>>> appreciated. >>>>> >>>>> The current thinking is that Application feels like the right >>>>> place to specify a global, application-wide >>>>> javafx.scene.control.MenuBar on. It could be assumed that if a >>>>> developer were to set this property, and the operating system upon >>>>> which the end-user was running the JavaFX application was Mac OS, >>>>> that the menubar will be displayed using the native Mac OS >>>>> menubar. Of course, if a developer wants a cross-platform look and >>>>> feel, they could just place the MenuBar in the stage as per usual >>>>> and it would display as it currently does. This approach opens up >>>>> a number of questions and issues: >>>>> >>>>> 1) What happens in the case of the end-user being on Windows? Is >>>>> the Application.MenuBar ignored, or is it automagically added to >>>>> the main Stage? (I would argue for totally ignoring it....but that >>>>> leads to the next point). >>>>> 2) This approach means there needs to be operating specific code >>>>> in the UI to test whether a non-native MenuBar should be added (in >>>>> the case of Windows, for example). This starts to clutter the UI >>>>> code, and without careful consideration by the developer may >>>>> result in needing to duplicate their MenuBar code. Is there a >>>>> better approach? >>>>> >>>>> Another place to specify a MenuBar would be on Stage, rather than >>>>> (or in addition to), Application. Having a MenuBar property on >>>>> Stage would allow for the MenuBar to change based on the currently >>>>> focused Stage - but I'm not certain this is desirable or even the >>>>> expected behaviour of Mac OS. Therefore, I'm thinking that this is >>>>> not likely to happen unless we hear otherwise. >>>>> >>>>> Like I said, we're at a very early exploration point in this >>>>> process. The controls team is very keen to hear feedback from the >>>>> community, as well as from the owners of the Application API, and >>>>> the Mac OS experts on this list. >>>>> >>>>> Thanks, >>>>> -- Jonathan > From steve.x.northover at oracle.com Mon Jan 9 10:59:11 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Mon, 09 Jan 2012 13:59:11 -0500 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: <4F0B35CA.7070008@oracle.com> References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> <4F0B3228.9050204@oracle.com> <4F0B35CA.7070008@oracle.com> Message-ID: <4F0B38FF.4050609@oracle.com> .. of course I meant "stage" when I said "shell". Steve On 09/01/2012 1:45 PM, steve.x.northover at oracle.com wrote: > Hello! > > I believe there are problems with 1) and 2). First off, there is no > way to mix and match. For example, suppose I want some shells to use > the global menu bar and others to use a local menu bar. Further, > application code may put trimmings around a menu bar and when the menu > bar is "hoisted up", the window might look strange. > > If we are doing 3), I think a better approach would be for the > application to decide whether it is using native menu bars or not by > explicitly setting a menu bar on a stage. It makes sense because on > platforms like Windows and GTK, there is a one-to-one mapping between > stage and menu bar so why not make this explicit in the API? One of > the problems with having API in Stage is that we don't want a > reference to MenuBar. Why don't we define something like > Stage.setMenyBarArea() or something like that to take a Node or > another type instead? > > Steve > > On 09/01/2012 1:30 PM, Paru Somashekar wrote: >> Hello All, >> >> The following is the proposal (follow up from previous email by >> Jonathan) from UI Controls team towards an API to support native Mac >> OS menubar. >> >> 1) A new property useGlobalMenuBar would be added to MenuBar class >> whose initial value will be set to a default dictated by a property >> settable via CSS ( and set to false by default). Once again, for the >> first cut of this support in this release, this will be the only >> public API for native menubar support. >> >> 2) In the case when we have multiple Menubars specified on a stage, >> the first one wins and will be made global, i.e. provided the >> platform supports native integration and useGlobalMenuBar is set to >> true. >> >> 3) As mentioned in a previous email by Jonathan, we will have hooks >> to switch between stages when they become active and swap menu bars. >> In the case when we don?t find a Menubar, it will be nulled out. This >> is the path we are choosing for now and we realize that we can change >> this later with no backward compatibility issues. >> We are extending the same concept to the scenario where, when the >> last window closes; it will be nulled out as well and hence there is >> no concept of a default menubar. >> >> thanks, >> Paru. >> >> On 12/14/11 5:20 PM, Kevin Rushforth wrote: >>> An API on Stage to set a menu bar has a certain elegance, plus it >>> gets around the backward compatibility and inconsistency problems >>> that having "native" on by default would create. Unfortunately, as >>> Rich pointed out earlier, it creates an unwanted dependency from a >>> modularity point of view since Stage will be in the base module and >>> should not depend on anything in controls. >>> >>> -- Kevin >>> >>> >>> steve.x.northover at oracle.com wrote: >>>> Hello all, >>>> >>>> How about an API in Stage where you set the menu bar for the stage? >>>> This is the menu that you wish to be native and the application >>>> very is clear about this. There can only be one real menu bar on >>>> Windows. Application code can ask for the menu bar and add items to >>>> it. If you have a property, application code needs to do the same >>>> search that FX does to determine if there is an active menu bar. >>>> >>>> Here is what I think about focus: On the Mac, when a stage gets >>>> focus, it sets the native menu bar. When it loses focus, it clears >>>> the native menu bar. Retaining the menu from the previous stage is >>>> unlikely to be the right thing to do as an application can have >>>> many different stages around and only the application can know >>>> whether the current menu bar applies to a stage. >>>> >>>> Steve >>>> >>>> On 14/12/2011 6:31 PM, Jonathan Giles wrote: >>>>> Hi All, >>>>> >>>>> Here's an update from the UI controls team as to how we see the >>>>> native Mac OS menubar support working. Your thoughts are appreciated. >>>>> >>>>> After discussing it again today, we think that the approach >>>>> suggested by Richard in an earlier email in this thread makes the >>>>> best sense, in terms of modularity and code cleanliness. I'll >>>>> explain this further shortly... >>>>> >>>>> The thinking is to add a new property to >>>>> javafx.scene.control.MenuBar. We haven't settled on a name, but >>>>> it's something along the lines of 'native', 'global', >>>>> 'globalMenuBar', 'screenMenuBar', or 'applicationMenuBar'. >>>>> Whatever property name we use, we'll expand it out to have the >>>>> usual set*/get*/*property methods. This would be the only public >>>>> API we end up adding for native menubar support. For the remainder >>>>> of this email I refer to this property as 'native'. >>>>> >>>>> This property will by default be true, indicating that on >>>>> platforms where we support native integration, it'll happen by >>>>> default. >>>>> >>>>> On a platform that supports native integration, we'll find the >>>>> 'first' MenuBar in the scene that has the 'native' property set to >>>>> true. We can't guarantee that we'll find necessarily the >>>>> physically top-most MenuBar as that is really a matter of how the >>>>> scenegraph is laid out. Of course, this is only a problem in >>>>> situations where the scene contains multiple MenuBars where >>>>> 'native' is true in more than one of them, which we hope won't >>>>> often be the case. If a Scene does have multiple MenuBars with >>>>> 'native' set to true, the behaviour is undefined. If the wrong >>>>> MenuBar is made native, you can help provide a hint by setting >>>>> 'native' to false in the relevant place(s). >>>>> >>>>> We'll also hook into the Stage and listen to the relevant events, >>>>> such that when a Stage gains focus, we'll switch in any native >>>>> menubars found in the scene of that stage. If no relevant MenuBar >>>>> is found, then we can either retain the MenuBar from the previous >>>>> stage, or null it out. I'm going to assume the former is by far >>>>> going to win this vote, but feel free to surprise me. >>>>> >>>>> Using this approach, developer code should be cleaner. Your user >>>>> interface should position a MenuBar where it makes sense for your >>>>> application, regardless of the operating system (normally at the >>>>> very top of your scene). On platforms where native integration is >>>>> supported, the JavaFX-rendered MenuBar will not be rendered >>>>> (although it'll likely remain in the scenegraph as a no-op >>>>> control). If the 'native' property changes, we'll flick between >>>>> the native and JavaFX-rendered MenuBar as expected. This approach >>>>> means there is no operating system dependent code in your user >>>>> interface. >>>>> >>>>> As I mentioned - we're totally open to discussion on any of these >>>>> points. Any thoughts? >>>>> >>>>> -- Jonathan >>>>> >>>>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: >>>>>> Hi all, >>>>>> >>>>>> One of the things we're planning to support in JavaFX 2.1 is the >>>>>> native Mac OS menubar. This email is intended primarily to >>>>>> discuss the API one expects to see to set a MenuBar in the native >>>>>> Mac OS menubar area. Your feedback is sought and will be very >>>>>> much appreciated. >>>>>> >>>>>> The current thinking is that Application feels like the right >>>>>> place to specify a global, application-wide >>>>>> javafx.scene.control.MenuBar on. It could be assumed that if a >>>>>> developer were to set this property, and the operating system >>>>>> upon which the end-user was running the JavaFX application was >>>>>> Mac OS, that the menubar will be displayed using the native Mac >>>>>> OS menubar. Of course, if a developer wants a cross-platform look >>>>>> and feel, they could just place the MenuBar in the stage as per >>>>>> usual and it would display as it currently does. This approach >>>>>> opens up a number of questions and issues: >>>>>> >>>>>> 1) What happens in the case of the end-user being on Windows? Is >>>>>> the Application.MenuBar ignored, or is it automagically added to >>>>>> the main Stage? (I would argue for totally ignoring it....but >>>>>> that leads to the next point). >>>>>> 2) This approach means there needs to be operating specific code >>>>>> in the UI to test whether a non-native MenuBar should be added >>>>>> (in the case of Windows, for example). This starts to clutter the >>>>>> UI code, and without careful consideration by the developer may >>>>>> result in needing to duplicate their MenuBar code. Is there a >>>>>> better approach? >>>>>> >>>>>> Another place to specify a MenuBar would be on Stage, rather than >>>>>> (or in addition to), Application. Having a MenuBar property on >>>>>> Stage would allow for the MenuBar to change based on the >>>>>> currently focused Stage - but I'm not certain this is desirable >>>>>> or even the expected behaviour of Mac OS. Therefore, I'm thinking >>>>>> that this is not likely to happen unless we hear otherwise. >>>>>> >>>>>> Like I said, we're at a very early exploration point in this >>>>>> process. The controls team is very keen to hear feedback from the >>>>>> community, as well as from the owners of the Application API, and >>>>>> the Mac OS experts on this list. >>>>>> >>>>>> Thanks, >>>>>> -- Jonathan >> From parvathi.somashekar at oracle.com Mon Jan 9 11:50:17 2012 From: parvathi.somashekar at oracle.com (Paru Somashekar) Date: Mon, 09 Jan 2012 11:50:17 -0800 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: <4F0B35CA.7070008@oracle.com> References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> <4F0B3228.9050204@oracle.com> <4F0B35CA.7070008@oracle.com> Message-ID: <4F0B44F9.2050007@oracle.com> On 1/9/12 10:45 AM, steve.x.northover at oracle.com wrote: > Hello! > > I believe there are problems with 1) and 2). First off, there is no > way to mix and match. For example, suppose I want some shells to use > the global menu bar and others to use a local menu bar. Further, > application code may put trimmings around a menu bar and when the menu > bar is "hoisted up", the window might look strange. The useGlobalMenuBar property on Menubar would not be a static property. So we could mix and match as the property is per MenuBar. > > If we are doing 3), I think a better approach would be for the > application to decide whether it is using native menu bars or not by > explicitly setting a menu bar on a stage. It makes sense because on > platforms like Windows and GTK, there is a one-to-one mapping between > stage and menu bar so why not make this explicit in the API? One of > the problems with having API in Stage is that we don't want a > reference to MenuBar. Why don't we define something like > Stage.setMenyBarArea() or something like that to take a Node or > another type instead? We were thinking that for this release, having a property on MenuBar which is set to false by default will ensure backwards and forwards compatibility (with minimal code changes for the application developer). If we want to change that to an API on Stage for a future release - it could be easily done without breaking compatibility. thanks, Paru. > > Steve > > On 09/01/2012 1:30 PM, Paru Somashekar wrote: >> Hello All, >> >> The following is the proposal (follow up from previous email by >> Jonathan) from UI Controls team towards an API to support native Mac >> OS menubar. >> >> 1) A new property useGlobalMenuBar would be added to MenuBar class >> whose initial value will be set to a default dictated by a property >> settable via CSS ( and set to false by default). Once again, for the >> first cut of this support in this release, this will be the only >> public API for native menubar support. >> >> 2) In the case when we have multiple Menubars specified on a stage, >> the first one wins and will be made global, i.e. provided the >> platform supports native integration and useGlobalMenuBar is set to >> true. >> >> 3) As mentioned in a previous email by Jonathan, we will have hooks >> to switch between stages when they become active and swap menu bars. >> In the case when we don?t find a Menubar, it will be nulled out. This >> is the path we are choosing for now and we realize that we can change >> this later with no backward compatibility issues. >> We are extending the same concept to the scenario where, when the >> last window closes; it will be nulled out as well and hence there is >> no concept of a default menubar. >> >> thanks, >> Paru. >> >> On 12/14/11 5:20 PM, Kevin Rushforth wrote: >>> An API on Stage to set a menu bar has a certain elegance, plus it >>> gets around the backward compatibility and inconsistency problems >>> that having "native" on by default would create. Unfortunately, as >>> Rich pointed out earlier, it creates an unwanted dependency from a >>> modularity point of view since Stage will be in the base module and >>> should not depend on anything in controls. >>> >>> -- Kevin >>> >>> >>> steve.x.northover at oracle.com wrote: >>>> Hello all, >>>> >>>> How about an API in Stage where you set the menu bar for the stage? >>>> This is the menu that you wish to be native and the application >>>> very is clear about this. There can only be one real menu bar on >>>> Windows. Application code can ask for the menu bar and add items to >>>> it. If you have a property, application code needs to do the same >>>> search that FX does to determine if there is an active menu bar. >>>> >>>> Here is what I think about focus: On the Mac, when a stage gets >>>> focus, it sets the native menu bar. When it loses focus, it clears >>>> the native menu bar. Retaining the menu from the previous stage is >>>> unlikely to be the right thing to do as an application can have >>>> many different stages around and only the application can know >>>> whether the current menu bar applies to a stage. >>>> >>>> Steve >>>> >>>> On 14/12/2011 6:31 PM, Jonathan Giles wrote: >>>>> Hi All, >>>>> >>>>> Here's an update from the UI controls team as to how we see the >>>>> native Mac OS menubar support working. Your thoughts are appreciated. >>>>> >>>>> After discussing it again today, we think that the approach >>>>> suggested by Richard in an earlier email in this thread makes the >>>>> best sense, in terms of modularity and code cleanliness. I'll >>>>> explain this further shortly... >>>>> >>>>> The thinking is to add a new property to >>>>> javafx.scene.control.MenuBar. We haven't settled on a name, but >>>>> it's something along the lines of 'native', 'global', >>>>> 'globalMenuBar', 'screenMenuBar', or 'applicationMenuBar'. >>>>> Whatever property name we use, we'll expand it out to have the >>>>> usual set*/get*/*property methods. This would be the only public >>>>> API we end up adding for native menubar support. For the remainder >>>>> of this email I refer to this property as 'native'. >>>>> >>>>> This property will by default be true, indicating that on >>>>> platforms where we support native integration, it'll happen by >>>>> default. >>>>> >>>>> On a platform that supports native integration, we'll find the >>>>> 'first' MenuBar in the scene that has the 'native' property set to >>>>> true. We can't guarantee that we'll find necessarily the >>>>> physically top-most MenuBar as that is really a matter of how the >>>>> scenegraph is laid out. Of course, this is only a problem in >>>>> situations where the scene contains multiple MenuBars where >>>>> 'native' is true in more than one of them, which we hope won't >>>>> often be the case. If a Scene does have multiple MenuBars with >>>>> 'native' set to true, the behaviour is undefined. If the wrong >>>>> MenuBar is made native, you can help provide a hint by setting >>>>> 'native' to false in the relevant place(s). >>>>> >>>>> We'll also hook into the Stage and listen to the relevant events, >>>>> such that when a Stage gains focus, we'll switch in any native >>>>> menubars found in the scene of that stage. If no relevant MenuBar >>>>> is found, then we can either retain the MenuBar from the previous >>>>> stage, or null it out. I'm going to assume the former is by far >>>>> going to win this vote, but feel free to surprise me. >>>>> >>>>> Using this approach, developer code should be cleaner. Your user >>>>> interface should position a MenuBar where it makes sense for your >>>>> application, regardless of the operating system (normally at the >>>>> very top of your scene). On platforms where native integration is >>>>> supported, the JavaFX-rendered MenuBar will not be rendered >>>>> (although it'll likely remain in the scenegraph as a no-op >>>>> control). If the 'native' property changes, we'll flick between >>>>> the native and JavaFX-rendered MenuBar as expected. This approach >>>>> means there is no operating system dependent code in your user >>>>> interface. >>>>> >>>>> As I mentioned - we're totally open to discussion on any of these >>>>> points. Any thoughts? >>>>> >>>>> -- Jonathan >>>>> >>>>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: >>>>>> Hi all, >>>>>> >>>>>> One of the things we're planning to support in JavaFX 2.1 is the >>>>>> native Mac OS menubar. This email is intended primarily to >>>>>> discuss the API one expects to see to set a MenuBar in the native >>>>>> Mac OS menubar area. Your feedback is sought and will be very >>>>>> much appreciated. >>>>>> >>>>>> The current thinking is that Application feels like the right >>>>>> place to specify a global, application-wide >>>>>> javafx.scene.control.MenuBar on. It could be assumed that if a >>>>>> developer were to set this property, and the operating system >>>>>> upon which the end-user was running the JavaFX application was >>>>>> Mac OS, that the menubar will be displayed using the native Mac >>>>>> OS menubar. Of course, if a developer wants a cross-platform look >>>>>> and feel, they could just place the MenuBar in the stage as per >>>>>> usual and it would display as it currently does. This approach >>>>>> opens up a number of questions and issues: >>>>>> >>>>>> 1) What happens in the case of the end-user being on Windows? Is >>>>>> the Application.MenuBar ignored, or is it automagically added to >>>>>> the main Stage? (I would argue for totally ignoring it....but >>>>>> that leads to the next point). >>>>>> 2) This approach means there needs to be operating specific code >>>>>> in the UI to test whether a non-native MenuBar should be added >>>>>> (in the case of Windows, for example). This starts to clutter the >>>>>> UI code, and without careful consideration by the developer may >>>>>> result in needing to duplicate their MenuBar code. Is there a >>>>>> better approach? >>>>>> >>>>>> Another place to specify a MenuBar would be on Stage, rather than >>>>>> (or in addition to), Application. Having a MenuBar property on >>>>>> Stage would allow for the MenuBar to change based on the >>>>>> currently focused Stage - but I'm not certain this is desirable >>>>>> or even the expected behaviour of Mac OS. Therefore, I'm thinking >>>>>> that this is not likely to happen unless we hear otherwise. >>>>>> >>>>>> Like I said, we're at a very early exploration point in this >>>>>> process. The controls team is very keen to hear feedback from the >>>>>> community, as well as from the owners of the Application API, and >>>>>> the Mac OS experts on this list. >>>>>> >>>>>> Thanks, >>>>>> -- Jonathan >> From steve.x.northover at oracle.com Mon Jan 9 12:11:55 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Mon, 09 Jan 2012 15:11:55 -0500 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: <4F0B44F9.2050007@oracle.com> References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> <4F0B3228.9050204@oracle.com> <4F0B35CA.7070008@oracle.com> <4F0B44F9.2050007@oracle.com> Message-ID: <4F0B4A0B.1090107@oracle.com> Having the property on MenuBar rather than the API on stage means that you can set the property on two menu bars at the same time and FX must choose one. There can't be two native menu bars at the same time on the same stage so why have API that allows it? Having an API in stage means that an application can easily query what the current menu bar is or whether there is a menu bar rather than performing the same search that FX does. I suppose that you could add API in stage later but that would mean two sets of API to do the same thing and the question about which one would win when they are mixed and matched. Steve On 09/01/2012 2:50 PM, Paru Somashekar wrote: > On 1/9/12 10:45 AM, steve.x.northover at oracle.com wrote: >> Hello! >> >> I believe there are problems with 1) and 2). First off, there is no >> way to mix and match. For example, suppose I want some shells to use >> the global menu bar and others to use a local menu bar. Further, >> application code may put trimmings around a menu bar and when the >> menu bar is "hoisted up", the window might look strange. > The useGlobalMenuBar property on Menubar would not be a static > property. So we could mix and match as the property is per MenuBar. >> >> If we are doing 3), I think a better approach would be for the >> application to decide whether it is using native menu bars or not by >> explicitly setting a menu bar on a stage. It makes sense because on >> platforms like Windows and GTK, there is a one-to-one mapping between >> stage and menu bar so why not make this explicit in the API? One of >> the problems with having API in Stage is that we don't want a >> reference to MenuBar. Why don't we define something like >> Stage.setMenyBarArea() or something like that to take a Node or >> another type instead? > We were thinking that for this release, having a property on MenuBar > which is set to false by default will ensure backwards and forwards > compatibility (with minimal code changes for the application > developer). If we want to change that to an API on Stage for a future > release - it could be easily done without breaking compatibility. > > thanks, > Paru. > >> >> Steve >> >> On 09/01/2012 1:30 PM, Paru Somashekar wrote: >>> Hello All, >>> >>> The following is the proposal (follow up from previous email by >>> Jonathan) from UI Controls team towards an API to support native Mac >>> OS menubar. >>> >>> 1) A new property useGlobalMenuBar would be added to MenuBar class >>> whose initial value will be set to a default dictated by a property >>> settable via CSS ( and set to false by default). Once again, for the >>> first cut of this support in this release, this will be the only >>> public API for native menubar support. >>> >>> 2) In the case when we have multiple Menubars specified on a stage, >>> the first one wins and will be made global, i.e. provided the >>> platform supports native integration and useGlobalMenuBar is set to >>> true. >>> >>> 3) As mentioned in a previous email by Jonathan, we will have hooks >>> to switch between stages when they become active and swap menu bars. >>> In the case when we don?t find a Menubar, it will be nulled out. >>> This is the path we are choosing for now and we realize that we can >>> change this later with no backward compatibility issues. >>> We are extending the same concept to the scenario where, when the >>> last window closes; it will be nulled out as well and hence there is >>> no concept of a default menubar. >>> >>> thanks, >>> Paru. >>> >>> On 12/14/11 5:20 PM, Kevin Rushforth wrote: >>>> An API on Stage to set a menu bar has a certain elegance, plus it >>>> gets around the backward compatibility and inconsistency problems >>>> that having "native" on by default would create. Unfortunately, as >>>> Rich pointed out earlier, it creates an unwanted dependency from a >>>> modularity point of view since Stage will be in the base module and >>>> should not depend on anything in controls. >>>> >>>> -- Kevin >>>> >>>> >>>> steve.x.northover at oracle.com wrote: >>>>> Hello all, >>>>> >>>>> How about an API in Stage where you set the menu bar for the >>>>> stage? This is the menu that you wish to be native and the >>>>> application very is clear about this. There can only be one real >>>>> menu bar on Windows. Application code can ask for the menu bar and >>>>> add items to it. If you have a property, application code needs to >>>>> do the same search that FX does to determine if there is an active >>>>> menu bar. >>>>> >>>>> Here is what I think about focus: On the Mac, when a stage gets >>>>> focus, it sets the native menu bar. When it loses focus, it clears >>>>> the native menu bar. Retaining the menu from the previous stage is >>>>> unlikely to be the right thing to do as an application can have >>>>> many different stages around and only the application can know >>>>> whether the current menu bar applies to a stage. >>>>> >>>>> Steve >>>>> >>>>> On 14/12/2011 6:31 PM, Jonathan Giles wrote: >>>>>> Hi All, >>>>>> >>>>>> Here's an update from the UI controls team as to how we see the >>>>>> native Mac OS menubar support working. Your thoughts are >>>>>> appreciated. >>>>>> >>>>>> After discussing it again today, we think that the approach >>>>>> suggested by Richard in an earlier email in this thread makes the >>>>>> best sense, in terms of modularity and code cleanliness. I'll >>>>>> explain this further shortly... >>>>>> >>>>>> The thinking is to add a new property to >>>>>> javafx.scene.control.MenuBar. We haven't settled on a name, but >>>>>> it's something along the lines of 'native', 'global', >>>>>> 'globalMenuBar', 'screenMenuBar', or 'applicationMenuBar'. >>>>>> Whatever property name we use, we'll expand it out to have the >>>>>> usual set*/get*/*property methods. This would be the only public >>>>>> API we end up adding for native menubar support. For the >>>>>> remainder of this email I refer to this property as 'native'. >>>>>> >>>>>> This property will by default be true, indicating that on >>>>>> platforms where we support native integration, it'll happen by >>>>>> default. >>>>>> >>>>>> On a platform that supports native integration, we'll find the >>>>>> 'first' MenuBar in the scene that has the 'native' property set >>>>>> to true. We can't guarantee that we'll find necessarily the >>>>>> physically top-most MenuBar as that is really a matter of how the >>>>>> scenegraph is laid out. Of course, this is only a problem in >>>>>> situations where the scene contains multiple MenuBars where >>>>>> 'native' is true in more than one of them, which we hope won't >>>>>> often be the case. If a Scene does have multiple MenuBars with >>>>>> 'native' set to true, the behaviour is undefined. If the wrong >>>>>> MenuBar is made native, you can help provide a hint by setting >>>>>> 'native' to false in the relevant place(s). >>>>>> >>>>>> We'll also hook into the Stage and listen to the relevant events, >>>>>> such that when a Stage gains focus, we'll switch in any native >>>>>> menubars found in the scene of that stage. If no relevant MenuBar >>>>>> is found, then we can either retain the MenuBar from the previous >>>>>> stage, or null it out. I'm going to assume the former is by far >>>>>> going to win this vote, but feel free to surprise me. >>>>>> >>>>>> Using this approach, developer code should be cleaner. Your user >>>>>> interface should position a MenuBar where it makes sense for your >>>>>> application, regardless of the operating system (normally at the >>>>>> very top of your scene). On platforms where native integration is >>>>>> supported, the JavaFX-rendered MenuBar will not be rendered >>>>>> (although it'll likely remain in the scenegraph as a no-op >>>>>> control). If the 'native' property changes, we'll flick between >>>>>> the native and JavaFX-rendered MenuBar as expected. This approach >>>>>> means there is no operating system dependent code in your user >>>>>> interface. >>>>>> >>>>>> As I mentioned - we're totally open to discussion on any of these >>>>>> points. Any thoughts? >>>>>> >>>>>> -- Jonathan >>>>>> >>>>>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: >>>>>>> Hi all, >>>>>>> >>>>>>> One of the things we're planning to support in JavaFX 2.1 is the >>>>>>> native Mac OS menubar. This email is intended primarily to >>>>>>> discuss the API one expects to see to set a MenuBar in the >>>>>>> native Mac OS menubar area. Your feedback is sought and will be >>>>>>> very much appreciated. >>>>>>> >>>>>>> The current thinking is that Application feels like the right >>>>>>> place to specify a global, application-wide >>>>>>> javafx.scene.control.MenuBar on. It could be assumed that if a >>>>>>> developer were to set this property, and the operating system >>>>>>> upon which the end-user was running the JavaFX application was >>>>>>> Mac OS, that the menubar will be displayed using the native Mac >>>>>>> OS menubar. Of course, if a developer wants a cross-platform >>>>>>> look and feel, they could just place the MenuBar in the stage as >>>>>>> per usual and it would display as it currently does. This >>>>>>> approach opens up a number of questions and issues: >>>>>>> >>>>>>> 1) What happens in the case of the end-user being on Windows? Is >>>>>>> the Application.MenuBar ignored, or is it automagically added to >>>>>>> the main Stage? (I would argue for totally ignoring it....but >>>>>>> that leads to the next point). >>>>>>> 2) This approach means there needs to be operating specific code >>>>>>> in the UI to test whether a non-native MenuBar should be added >>>>>>> (in the case of Windows, for example). This starts to clutter >>>>>>> the UI code, and without careful consideration by the developer >>>>>>> may result in needing to duplicate their MenuBar code. Is there >>>>>>> a better approach? >>>>>>> >>>>>>> Another place to specify a MenuBar would be on Stage, rather >>>>>>> than (or in addition to), Application. Having a MenuBar property >>>>>>> on Stage would allow for the MenuBar to change based on the >>>>>>> currently focused Stage - but I'm not certain this is desirable >>>>>>> or even the expected behaviour of Mac OS. Therefore, I'm >>>>>>> thinking that this is not likely to happen unless we hear >>>>>>> otherwise. >>>>>>> >>>>>>> Like I said, we're at a very early exploration point in this >>>>>>> process. The controls team is very keen to hear feedback from >>>>>>> the community, as well as from the owners of the Application >>>>>>> API, and the Mac OS experts on this list. >>>>>>> >>>>>>> Thanks, >>>>>>> -- Jonathan >>> > From richard.bair at oracle.com Mon Jan 9 15:11:20 2012 From: richard.bair at oracle.com (Richard Bair) Date: Mon, 9 Jan 2012 15:11:20 -0800 Subject: Review: ScheduledService In-Reply-To: <84AB2BC0-0956-4881-8A85-2BB0D40FD064@oracle.com> References: <84AB2BC0-0956-4881-8A85-2BB0D40FD064@oracle.com> Message-ID: <339100AD-AE34-4F25-8502-10F7DCFA0B4C@oracle.com> I've updated the issue with the latest patch. Just an FYI but I'm not comfortable right now adding this for 2.1 unless somebody is really interested and will invest the time to get it reviewed properly. Otherwise I'll just leave the patch there and move it to 2.2. Cheers Richard On Jan 6, 2012, at 3:47 PM, Richard Bair wrote: > Here's another one that needs review. The patch supplied with the issue is pretty detailed (its the whole implementation + documentation). The big thing to look for here is going to be semantic incompatibilities with Service (I don't think there are any) or any weirdness in the API. There are some pretty cool features on this class, I think. > > http://javafx-jira.kenai.com/browse/RT-18702 > > Thanks > Richard From richard.bair at oracle.com Mon Jan 9 15:12:49 2012 From: richard.bair at oracle.com (Richard Bair) Date: Mon, 9 Jan 2012 15:12:49 -0800 Subject: Incremental updates from Tasks In-Reply-To: <470804F4-4D60-487A-85A9-8472F939D230@oracle.com> References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> <1325883654.3075.5.camel@moonlight> <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> <1325885304.3075.26.camel@moonlight> <72CB0F3D-2D1C-442E-B65F-20EF73E7D8C9@oracle.com> <470804F4-4D60-487A-85A9-8472F939D230@oracle.com> Message-ID: I've attached the latest full patch with tests to the issue (http://javafx-jira.kenai.com/browse/RT-18820). I'll retarget it to 2.2 unless somebody is available to do an in depth review and let me know what you think. I think it is all set and ready to go, the only thing that kind of bothers me is the name ObservableListTask and the "process" name, since I have used "update" for the same functionality in Task / TaskBase. Thanks Richard On Jan 6, 2012, at 3:40 PM, Richard Bair wrote: > I tried the following. I created a TaskBase, and moved almost everything from Task to TaskBase. Task now extends TaskBase, and adds: > > updateValue(V value); > abstract V call() throws Exception; > > It is entirely compatible with the previous version and all the unit tests still pass. The only difference is that the method we expose (call) returns a value and is declared on Task instead of TaskBase. Also, the updateValue is only on Task. > > ObservableListTask also extends TaskBase, and adds: > > protected abstract void call() throws Exception; > protected void publish(E... items); > protected void publish(Collection items); > > I just used the SwingWorker name "publish", but there is no process. Whatever you publish gets added. We could add a process with a default implementation if we wanted to (allowing some users to filter results or whatnot, but I'm not sure there is a reason you want to do that on the FX thread instead of the background thread, so I would propose leaving it off for now). > > This call() method returns no value, just the way it should be. You just call publish to put add items. Since the Task is a one-shot deal, there isn't really a compelling reason to add other methods like clear() (you could also do your own runLater() if you needed to do something tricky). > > What do you think? Here is an example of usage (complete with fading rectangles :-)): > > > ObservableListTask task = new ObservableListTask() { > @Override > protected void call() throws Exception { > for (int i=0; i<900; i++) { > Rectangle r = new Rectangle(10, 10); > r.setFill(Color.color(Math.random(), Math.random(), Math.random())); > r.setOpacity(0); > FadeTransition tx = new FadeTransition(Duration.seconds(1)); > tx.setToValue(1.0); > tx.setNode(r); > tx.play(); > publish(r); > Thread.sleep(20); > updateProgress(i, 900); > } > } > }; > > Richard > From hang.vo at oracle.com Tue Jan 10 00:33:05 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 10 Jan 2012 08:33:05 +0000 Subject: hg: openjfx/2.1/master/rt: 5 new changesets Message-ID: <20120110083307.57522478FD@hg.openjdk.java.net> Changeset: 49a22b095c59 Author: jgiles Date: 2012-01-09 14:49 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/49a22b095c59 A few more keyboard navigation tests for ListView and TableView, and a few minor typo fixes in TableViewBehavior identified by tests. ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java ! javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java + javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java Changeset: c2829806d92d Author: jgiles Date: 2012-01-09 14:58 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c2829806d92d RT-18690: [ListView] page works wrong: step is always of 1 element. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java Changeset: c411c4c0ba62 Author: jgiles Date: 2012-01-09 16:41 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c411c4c0ba62 RT-18513: ListView, cell edition and call to items.setAll leads to stack overflow ! javafx-ui-controls/src/javafx/scene/control/ListCell.java ! javafx-ui-controls/src/javafx/scene/control/TableCell.java ! javafx-ui-controls/src/javafx/scene/control/TreeCell.java Changeset: 2351e0099f49 Author: jgiles Date: 2012-01-10 21:16 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/2351e0099f49 Partial progress on RT-18851: UX test cases for keyboard navigation in ListView (Essentially creating a bunch of unit tests to ensure proper keyboard navigation support in ListView) ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListCellBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java + javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/ListViewAnchorRetriever.java + javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/TableViewAnchorRetriever.java + javafx-ui-controls/test/com/sun/javafx/scene/control/behavior/TreeViewAnchorRetriever.java ! javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java ! javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java + javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java Changeset: 3a90e72e4f4d Author: jgiles Date: 2012-01-10 21:16 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3a90e72e4f4d Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From hang.vo at oracle.com Tue Jan 10 09:22:57 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 10 Jan 2012 17:22:57 +0000 Subject: hg: openjfx/2.1/master/rt: [TEST ONLY] Comment out usage of RunAwayTask in TaskCancelTest pending RT-18864 Message-ID: <20120110172257.5A53247907@hg.openjdk.java.net> Changeset: 93219a930ca6 Author: David Grieve Date: 2012-01-10 12:16 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/93219a930ca6 [TEST ONLY] Comment out usage of RunAwayTask in TaskCancelTest pending RT-18864 ! javafx-concurrent/test/javafx/concurrent/TaskCancelTest.java From hang.vo at oracle.com Tue Jan 10 13:33:41 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 10 Jan 2012 21:33:41 +0000 Subject: hg: openjfx/2.1/master/rt: Fix for RT-18864: TaskCancelTest.java does not compile Message-ID: <20120110213342.33F2C4790B@hg.openjdk.java.net> Changeset: e142d94c6c05 Author: rbair Date: 2012-01-10 13:25 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e142d94c6c05 Fix for RT-18864: TaskCancelTest.java does not compile + javafx-concurrent/test/javafx/concurrent/mocks/RunAwayTask.java From hang.vo at oracle.com Tue Jan 10 17:52:48 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 11 Jan 2012 01:52:48 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18864: RunAwayTask was added, but the commented out code in TaskCancelTest was not restored. Message-ID: <20120111015248.B59FB47916@hg.openjdk.java.net> Changeset: 0a4321c889a3 Author: David Grieve Date: 2012-01-10 20:46 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/0a4321c889a3 RT-18864: RunAwayTask was added, but the commented out code in TaskCancelTest was not restored. ! javafx-concurrent/test/javafx/concurrent/TaskCancelTest.java From hang.vo at oracle.com Tue Jan 10 18:03:03 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 11 Jan 2012 02:03:03 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18864: RuTaskCancelTest still did not compile since RunAwayTask is abstract. Added missing method as no-op. Message-ID: <20120111020303.DF63147917@hg.openjdk.java.net> Changeset: d2e3815f73f9 Author: David Grieve Date: 2012-01-10 20:52 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/d2e3815f73f9 RT-18864: RuTaskCancelTest still did not compile since RunAwayTask is abstract. Added missing method as no-op. ! javafx-concurrent/test/javafx/concurrent/TaskCancelTest.java From hang.vo at oracle.com Wed Jan 11 07:12:38 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 11 Jan 2012 15:12:38 +0000 Subject: hg: openjfx/2.1/master/rt: 5 new changesets Message-ID: <20120111151239.BA5224791F@hg.openjdk.java.net> Changeset: 66e8f9e657cb Author: leifs Date: 2012-01-06 12:58 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/66e8f9e657cb RT-18517: Updated GlobalMenuAdapter to forward action and show/hide events. ! javafx-ui-controls/src/com/sun/javafx/scene/control/GlobalMenuAdapter.java Changeset: 5e02136636cb Author: Kevin Rushforth Date: 2012-01-09 14:33 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/5e02136636cb Fix broken build (add new Toolkit methods to StubToolkit) ! test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java Changeset: 6e9c0e567a9b Author: kcr Date: 2012-01-10 06:59 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6e9c0e567a9b RT-15221: Need a way to set minimum and maximum size of a window ! test-stub-toolkit/src/com/sun/javafx/pgstub/StubStage.java Changeset: 42d3501de782 Author: jpgodine at JPGODINE-LAP.st-users.us.oracle.com Date: 2012-01-10 10:58 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/42d3501de782 Automated merge with ssh://jpgodine at jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt Changeset: f5523d287d74 Author: David Grieve Date: 2012-01-11 09:52 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/f5523d287d74 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt From hang.vo at oracle.com Wed Jan 11 10:03:27 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 11 Jan 2012 18:03:27 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18844 - ScrollPane: scrollbars should show appear when the content has been resized to it minWidth Message-ID: <20120111180328.030DD47924@hg.openjdk.java.net> Changeset: 987e86f0a47a Author: mickf Date: 2012-01-11 17:56 +0000 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/987e86f0a47a RT-18844 - ScrollPane: scrollbars should show appear when the content has been resized to it minWidth ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollPaneSkin.java ! javafx-ui-controls/test/com/sun/javafx/scene/control/skin/ScrollPaneSkinTest.java From hang.vo at oracle.com Wed Jan 11 12:33:51 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 11 Jan 2012 20:33:51 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120111203352.1BBC747927@hg.openjdk.java.net> Changeset: e1eab6a71090 Author: jgiles Date: 2012-01-12 08:52 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e1eab6a71090 Further progress on RT-18851: UX test cases for keyboard navigation in ListView/TreeView/TableView (Essentially creating a bunch of unit tests to ensure proper keyboard navigation support) ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListCellBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableCellBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java ! javafx-ui-controls/test/javafx/scene/control/KeyModifier.java ! javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java ! javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java ! javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java Changeset: 85e7b7f8c79b Author: jgiles Date: 2012-01-12 08:54 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/85e7b7f8c79b Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From parvathi.somashekar at oracle.com Wed Jan 11 15:59:46 2012 From: parvathi.somashekar at oracle.com (Paru Somashekar) Date: Wed, 11 Jan 2012 15:59:46 -0800 Subject: Two ChoiceBox API additions Message-ID: <4F0E2272.5050704@oracle.com> The controls team is planning to add 2 new APIs to ChoiceBox control. They are :- 1. valueProperty : (getValue(), setValue() and valueProperty()) The value of a ChoiceBox is defined as the selected item in the ChoiceBox selection model. The valueProperty is synchronized with the selectedItem, in that when a user sets the value property the selected item/index is updated accordingly. And when selection changes, the value property also reflects the change. This property allows for bi-directional binding of external properties to the ChoiceBox and updates the selection model accordingly. 2. converterProperty : (getConverter(), setConverter(), converterProperty()) Allows a way to specify how to represent objects in the items list. When a StringConverter is set, the object toString method is not called and instead its toString(object T) is called, passing the objects in the items list. This is useful when using domain objects in a ChoiceBox as this property allows for customization of the representation. Developers can set any of the pre-built Converters available in the javafx.util.converter package. thanks, Paru. From hang.vo at oracle.com Wed Jan 11 18:42:42 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 12 Jan 2012 02:42:42 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18904: Controls.ListView-Rotate performance dropped up to 0 fps in fx2.1-b08 Message-ID: <20120112024242.E82EF47931@hg.openjdk.java.net> Changeset: 53f7d8f7ca5e Author: jgiles Date: 2012-01-12 14:21 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/53f7d8f7ca5e RT-18904: Controls.ListView-Rotate performance dropped up to 0 fps in fx2.1-b08 ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java From hang.vo at oracle.com Wed Jan 11 19:12:38 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 12 Jan 2012 03:12:38 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18905: -fx-alignment appears in StackPane. In the old way of handling styles, ToolBarSkin was able to intercept that property by overriding impl_cssSet. Now, ToolBarSkin presents its own alignment StyleableProperty. Message-ID: <20120112031238.E4E1947932@hg.openjdk.java.net> Changeset: 4e697a25d882 Author: David Grieve Date: 2012-01-11 22:04 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/4e697a25d882 RT-18905: -fx-alignment appears in StackPane. In the old way of handling styles, ToolBarSkin was able to intercept that property by overriding impl_cssSet. Now, ToolBarSkin presents its own alignment StyleableProperty. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ToolBarSkin.java From zonski at googlemail.com Wed Jan 11 19:12:50 2012 From: zonski at googlemail.com (Daniel Zwolenski) Date: Thu, 12 Jan 2012 13:12:50 +1000 Subject: Incremental updates from Tasks In-Reply-To: References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> <1325883654.3075.5.camel@moonlight> <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> <1325885304.3075.26.camel@moonlight> <72CB0F3D-2D1C-442E-B65F-20EF73E7D8C9@oracle.com> <470804F4-4D60-487A-85A9-8472F939D230@oracle.com> Message-ID: I haven't had much time to look into this in detail, but I'm wondering if we really need to change the Task API at all for this (and we seem to be ignoring Service in the above no?) Don't we just want a thread safe channel that we can publish data into, does it need to built into Task? If instead we had a separate generic/utility class for this then the Task and the View (or whatever handles the published data) could just share an instance of this and listen to it accordingly. final ThreadSafeEventChannel channel = new ThreadSafeEventChannel(); channel.setConsumer(new Consumer() { void handle(DataType data) { // handle published data } } Task t = new Task() { Whatever call() { // do some stuff channel.publish(new DataType(...)); } } Possibly the channel could even be an 'ObservableQueue', similar to ObservableList (although need to be careful with the API and threading). Maybe there is a case for having 'thread safe' versions of all the collection classes, so you could create them like so: ObservableList list = FXCollections.threadSafeObservableArrayList(); ObservableMap map = FXCollections.threadSafeObservableHashMap(); Where the calls/callbacks for them are all mapped into the FXT as needed. Could also be extended to properties (e.g. ThreadSafeStringProperty). The Task could then use several queues, properties, collections or whatever to publish its data in whatever way makes sense to it. Just some thoughts. On Tue, Jan 10, 2012 at 9:12 AM, Richard Bair wrote: > I've attached the latest full patch with tests to the issue ( > http://javafx-jira.kenai.com/browse/RT-18820). I'll retarget it to 2.2 > unless somebody is available to do an in depth review and let me know what > you think. I think it is all set and ready to go, the only thing that kind > of bothers me is the name ObservableListTask and the "process" name, since > I have used "update" for the same functionality in Task / TaskBase. > > Thanks > Richard > > On Jan 6, 2012, at 3:40 PM, Richard Bair wrote: > > > I tried the following. I created a TaskBase, and moved almost everything > from Task to TaskBase. Task now extends TaskBase, and adds: > > > > updateValue(V value); > > abstract V call() throws Exception; > > > > It is entirely compatible with the previous version and all the unit > tests still pass. The only difference is that the method we expose (call) > returns a value and is declared on Task instead of TaskBase. Also, the > updateValue is only on Task. > > > > ObservableListTask also extends TaskBase, and adds: > > > > protected abstract void call() throws Exception; > > protected void publish(E... items); > > protected void publish(Collection items); > > > > I just used the SwingWorker name "publish", but there is no process. > Whatever you publish gets added. We could add a process with a default > implementation if we wanted to (allowing some users to filter results or > whatnot, but I'm not sure there is a reason you want to do that on the FX > thread instead of the background thread, so I would propose leaving it off > for now). > > > > This call() method returns no value, just the way it should be. You just > call publish to put add items. Since the Task is a one-shot deal, there > isn't really a compelling reason to add other methods like clear() (you > could also do your own runLater() if you needed to do something tricky). > > > > What do you think? Here is an example of usage (complete with fading > rectangles :-)): > > > > > > ObservableListTask task = new > ObservableListTask() { > > @Override > > protected void call() throws Exception { > > for (int i=0; i<900; i++) { > > Rectangle r = new Rectangle(10, 10); > > r.setFill(Color.color(Math.random(), Math.random(), > Math.random())); > > r.setOpacity(0); > > FadeTransition tx = new > FadeTransition(Duration.seconds(1)); > > tx.setToValue(1.0); > > tx.setNode(r); > > tx.play(); > > publish(r); > > Thread.sleep(20); > > updateProgress(i, 900); > > } > > } > > }; > > > > Richard > > > > From tom.schindl at bestsolution.at Wed Jan 11 23:40:41 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Thu, 12 Jan 2012 08:40:41 +0100 Subject: Two ChoiceBox API additions In-Reply-To: <4F0E2272.5050704@oracle.com> References: <4F0E2272.5050704@oracle.com> Message-ID: <4F0E8E79.3020502@bestsolution.at> Am 12.01.12 00:59, schrieb Paru Somashekar: > The controls team is planning to add 2 new APIs to ChoiceBox control. > They are :- > > 1. valueProperty : (getValue(), setValue() and valueProperty()) > The value of a ChoiceBox is defined as the selected item in the > ChoiceBox selection model. The valueProperty is synchronized with the > selectedItem, in that when a user sets the value property the selected > item/index is updated accordingly. And when selection changes, the value > property also reflects the change. This property allows for > bi-directional binding of external properties to the ChoiceBox and > updates the selection model accordingly. > > 2. converterProperty : (getConverter(), setConverter(), > converterProperty()) Wouldn't it better named labelConverterProperty? Tom -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From hang.vo at oracle.com Wed Jan 11 23:52:42 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 12 Jan 2012 07:52:42 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-18656 Add support for StringConverter to ChoiceBox & Message-ID: <20120112075243.32FB547934@hg.openjdk.java.net> Changeset: 0ae14bbf91a1 Author: Paru Somashekar Date: 2012-01-11 23:52 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/0ae14bbf91a1 fix RT-18656 Add support for StringConverter to ChoiceBox & RT-18475 Support bi-directional binding for the value of a ChoiceBox. ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ChoiceBoxBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ChoiceBoxSkin.java ! javafx-ui-controls/src/javafx/scene/control/ChoiceBox.java ! javafx-ui-controls/test/javafx/scene/control/ChoiceBoxTest.java ! javafx-ui-controls/test/javafx/scene/control/ComboBoxTest.java From jonathan.giles at oracle.com Thu Jan 12 00:15:00 2012 From: jonathan.giles at oracle.com (Jonathan Giles) Date: Thu, 12 Jan 2012 21:15:00 +1300 Subject: Two ChoiceBox API additions In-Reply-To: <4F0E8E79.3020502@bestsolution.at> References: <4F0E2272.5050704@oracle.com> <4F0E8E79.3020502@bestsolution.at> Message-ID: <4F0E9684.1050307@oracle.com> The reason why it is named 'converter' is for consistency across the API. The intention is that future controls may also have a converter that is a StringConverter. Already ComboBox has this API (but of course uses both toString(..) and fromString(..), which ChoiceBox does not). I can see your thinking behind the name - it is more specifically what it is - but at the same time I personally like the improved discovery (and lessened mental overhead) of using consistent API naming. What do others think? -- Jonathan On Thursday, 12 January 2012 8:40:41 p.m., Tom Schindl wrote: > Am 12.01.12 00:59, schrieb Paru Somashekar: >> The controls team is planning to add 2 new APIs to ChoiceBox control. >> They are :- >> >> 1. valueProperty : (getValue(), setValue() and valueProperty()) >> The value of a ChoiceBox is defined as the selected item in the >> ChoiceBox selection model. The valueProperty is synchronized with the >> selectedItem, in that when a user sets the value property the selected >> item/index is updated accordingly. And when selection changes, the value >> property also reflects the change. This property allows for >> bi-directional binding of external properties to the ChoiceBox and >> updates the selection model accordingly. >> >> 2. converterProperty : (getConverter(), setConverter(), >> converterProperty()) > > Wouldn't it better named labelConverterProperty? > > Tom > From tom.schindl at bestsolution.at Thu Jan 12 00:52:07 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Thu, 12 Jan 2012 09:52:07 +0100 Subject: Two ChoiceBox API additions In-Reply-To: <4F0E9684.1050307@oracle.com> References: <4F0E2272.5050704@oracle.com> <4F0E8E79.3020502@bestsolution.at> <4F0E9684.1050307@oracle.com> Message-ID: <4F0E9F37.1020006@bestsolution.at> Hi Jonathan (now with the list included), Exactly this Text-Example made me think that the converter you are setting here is something different! The "converter" you are passing here is more a kind of label provider (this term is borrowed from SWT/JFace). Naming it labelConverter btw still leaves room for an imageConverter (though as discussed in the bug Richard thought ChoiceBox is not the right UI-Control I'm still not so sure about that). I think the term converter should only be used if to* and from* are used but that's maybe just me. I'm not sure on this one now but couldn't one think about providing a label converter to all structured controls (combobox, list, table, ...) and then it suddenly once more gets a well known concept. Tom Am 12.01.12 09:15, schrieb Jonathan Giles: > The reason why it is named 'converter' is for consistency across the > API. The intention is that future controls may also have a converter > that is a StringConverter. Already ComboBox has this API (but of course > uses both toString(..) and fromString(..), which ChoiceBox does not). > > I can see your thinking behind the name - it is more specifically what > it is - but at the same time I personally like the improved discovery > (and lessened mental overhead) of using consistent API naming. > > What do others think? > > -- Jonathan > > On Thursday, 12 January 2012 8:40:41 p.m., Tom Schindl wrote: >> Am 12.01.12 00:59, schrieb Paru Somashekar: >>> The controls team is planning to add 2 new APIs to ChoiceBox control. >>> They are :- >>> >>> 1. valueProperty : (getValue(), setValue() and valueProperty()) >>> The value of a ChoiceBox is defined as the selected item in the >>> ChoiceBox selection model. The valueProperty is synchronized with the >>> selectedItem, in that when a user sets the value property the selected >>> item/index is updated accordingly. And when selection changes, the value >>> property also reflects the change. This property allows for >>> bi-directional binding of external properties to the ChoiceBox and >>> updates the selection model accordingly. >>> >>> 2. converterProperty : (getConverter(), setConverter(), >>> converterProperty()) >> >> Wouldn't it better named labelConverterProperty? >> >> Tom >> -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From tom.schindl at bestsolution.at Thu Jan 12 03:50:33 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Thu, 12 Jan 2012 12:50:33 +0100 Subject: Two ChoiceBox API additions In-Reply-To: <4F0E9F37.1020006@bestsolution.at> References: <4F0E2272.5050704@oracle.com> <4F0E8E79.3020502@bestsolution.at> <4F0E9684.1050307@oracle.com> <4F0E9F37.1020006@bestsolution.at> Message-ID: <4F0EC909.8040803@bestsolution.at> Might I suggest now when rethinking that passing a StringConverter is wrong and it should be CallBack? So my suggested API: labelCallbackProperty: Callback it simply isn't a a converter because you never use it for String => Object. Tom Am 12.01.12 09:52, schrieb Tom Schindl: > Hi Jonathan (now with the list included), > > Exactly this Text-Example made me think that the converter you are > setting here is something different! > > The "converter" you are passing here is more a kind of label provider > (this term is borrowed from SWT/JFace). Naming it labelConverter btw > still leaves room for an imageConverter (though as discussed in the bug > Richard thought ChoiceBox is not the right UI-Control I'm still not so > sure about that). > > I think the term converter should only be used if to* and from* are used > but that's maybe just me. > > I'm not sure on this one now but couldn't one think about providing a > label converter to all structured controls (combobox, list, table, ...) > and then it suddenly once more gets a well known concept. > > Tom > > Am 12.01.12 09:15, schrieb Jonathan Giles: >> The reason why it is named 'converter' is for consistency across the >> API. The intention is that future controls may also have a converter >> that is a StringConverter. Already ComboBox has this API (but of course >> uses both toString(..) and fromString(..), which ChoiceBox does not). >> >> I can see your thinking behind the name - it is more specifically what >> it is - but at the same time I personally like the improved discovery >> (and lessened mental overhead) of using consistent API naming. >> >> What do others think? >> >> -- Jonathan >> >> On Thursday, 12 January 2012 8:40:41 p.m., Tom Schindl wrote: >>> Am 12.01.12 00:59, schrieb Paru Somashekar: >>>> The controls team is planning to add 2 new APIs to ChoiceBox control. >>>> They are :- >>>> >>>> 1. valueProperty : (getValue(), setValue() and valueProperty()) >>>> The value of a ChoiceBox is defined as the selected item in the >>>> ChoiceBox selection model. The valueProperty is synchronized with the >>>> selectedItem, in that when a user sets the value property the selected >>>> item/index is updated accordingly. And when selection changes, the value >>>> property also reflects the change. This property allows for >>>> bi-directional binding of external properties to the ChoiceBox and >>>> updates the selection model accordingly. >>>> >>>> 2. converterProperty : (getConverter(), setConverter(), >>>> converterProperty()) >>> >>> Wouldn't it better named labelConverterProperty? >>> >>> Tom >>> > > -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From richard.bair at oracle.com Thu Jan 12 11:23:31 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 12 Jan 2012 11:23:31 -0800 Subject: Two ChoiceBox API additions In-Reply-To: <4F0EC909.8040803@bestsolution.at> References: <4F0E2272.5050704@oracle.com> <4F0E8E79.3020502@bestsolution.at> <4F0E9684.1050307@oracle.com> <4F0E9F37.1020006@bestsolution.at> <4F0EC909.8040803@bestsolution.at> Message-ID: <9171676B-9109-4A48-913F-EA59F02158B8@oracle.com> I think that is less convenient though because there are a pile of pre-built converters for dealing with everything from dates to decimals. Being able to just reuse the known converters I think is a big win. Plus, it is "converting" it just isn't doing a two-way conversion, but that's OK. Richard On Jan 12, 2012, at 3:50 AM, Tom Schindl wrote: > Might I suggest now when rethinking that passing a StringConverter is > wrong and it should be CallBack? > > So my suggested API: > > labelCallbackProperty: Callback > > it simply isn't a a converter because you never use it for String => Object. > > Tom > > Am 12.01.12 09:52, schrieb Tom Schindl: >> Hi Jonathan (now with the list included), >> >> Exactly this Text-Example made me think that the converter you are >> setting here is something different! >> >> The "converter" you are passing here is more a kind of label provider >> (this term is borrowed from SWT/JFace). Naming it labelConverter btw >> still leaves room for an imageConverter (though as discussed in the bug >> Richard thought ChoiceBox is not the right UI-Control I'm still not so >> sure about that). >> >> I think the term converter should only be used if to* and from* are used >> but that's maybe just me. >> >> I'm not sure on this one now but couldn't one think about providing a >> label converter to all structured controls (combobox, list, table, ...) >> and then it suddenly once more gets a well known concept. >> >> Tom >> >> Am 12.01.12 09:15, schrieb Jonathan Giles: >>> The reason why it is named 'converter' is for consistency across the >>> API. The intention is that future controls may also have a converter >>> that is a StringConverter. Already ComboBox has this API (but of course >>> uses both toString(..) and fromString(..), which ChoiceBox does not). >>> >>> I can see your thinking behind the name - it is more specifically what >>> it is - but at the same time I personally like the improved discovery >>> (and lessened mental overhead) of using consistent API naming. >>> >>> What do others think? >>> >>> -- Jonathan >>> >>> On Thursday, 12 January 2012 8:40:41 p.m., Tom Schindl wrote: >>>> Am 12.01.12 00:59, schrieb Paru Somashekar: >>>>> The controls team is planning to add 2 new APIs to ChoiceBox control. >>>>> They are :- >>>>> >>>>> 1. valueProperty : (getValue(), setValue() and valueProperty()) >>>>> The value of a ChoiceBox is defined as the selected item in the >>>>> ChoiceBox selection model. The valueProperty is synchronized with the >>>>> selectedItem, in that when a user sets the value property the selected >>>>> item/index is updated accordingly. And when selection changes, the value >>>>> property also reflects the change. This property allows for >>>>> bi-directional binding of external properties to the ChoiceBox and >>>>> updates the selection model accordingly. >>>>> >>>>> 2. converterProperty : (getConverter(), setConverter(), >>>>> converterProperty()) >>>> >>>> Wouldn't it better named labelConverterProperty? >>>> >>>> Tom >>>> >> >> > > > -- > B e s t S o l u t i o n . a t EDV Systemhaus GmbH > ------------------------------------------------------------------------ > tom schindl gesch?ftsf?hrer/CEO > ------------------------------------------------------------------------ > eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 > http://www.BestSolution.at phone ++43 512 935834 From richard.bair at oracle.com Thu Jan 12 11:32:28 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 12 Jan 2012 11:32:28 -0800 Subject: Incremental updates from Tasks In-Reply-To: References: <90389FC6-4B4A-41DC-86EE-9FB03189F5CB@oracle.com> <1325883654.3075.5.camel@moonlight> <2F552BC9-3ECD-44F8-8427-01499D73AFF9@oracle.com> <1325885304.3075.26.camel@moonlight> <72CB0F3D-2D1C-442E-B65F-20EF73E7D8C9@oracle.com> <470804F4-4D60-487A-85A9-8472F939D230@oracle.com> Message-ID: <1135B919-5428-4003-BA36-3DC4C0629EB8@oracle.com> Hi Dan, > I haven't had much time to look into this in detail, but I'm wondering if we really need to change the Task API at all for this (and we seem to be ignoring Service in the above no?) Actually, I think we only have to solve this for Task, because a Service simply binds to the state on the task. So if you have a task which calls updateValue multiple times, the Service will likewise get its value updated multiple times. The only problem will be a Service which returns an ObservableList -- each reset would cause it to be null and then get a value again with the new Task (because every Task has its own ObservableList). It might be nicer to have an ObservableListService which has a single stable ObservableList as its value which then has its contents bound to the contents of the ObservableListTasks's list. > Don't we just want a thread safe channel that we can publish data into, does it need to built into Task? If instead we had a separate generic/utility class for this then the Task and the View (or whatever handles the published data) could just share an instance of this and listen to it accordingly. > > final ThreadSafeEventChannel channel = new ThreadSafeEventChannel(); > channel.setConsumer(new Consumer() { > void handle(DataType data) { > // handle published data > } > } > > Task t = new Task() { > Whatever call() { > // do some stuff > channel.publish(new DataType(...)); > } > } > > > Possibly the channel could even be an 'ObservableQueue', similar to ObservableList (although need to be careful with the API and threading). Maybe there is a case for having 'thread safe' versions of all the collection classes, so you could create them like so: > > ObservableList list = FXCollections.threadSafeObservableArrayList(); > ObservableMap map = FXCollections.threadSafeObservableHashMap(); > > Where the calls/callbacks for them are all mapped into the FXT as needed. > > Could also be extended to properties (e.g. ThreadSafeStringProperty). > > The Task could then use several queues, properties, collections or whatever to publish its data in whatever way makes sense to it. > > Just some thoughts. Interesting idea. I think you still want "updateValue" on the Task for just setting what the value is -- having a channel or whatnot I think is overkill. And to do this right, you need to have some collection then which knows about the FX thread. So add a thread-safe ObservableList to the javafx.concurrent package, and then your Task can just use that instead of the standard ObservableList and all is well. I really like that idea. A lot. Gets rid of this gnarly ObservableListTask. However, it doesn't help with the "ObservableListService" -- in that case you still would get new ObservableList guys all the time. Something else to think about. Richard From kinsley.wong at oracle.com Thu Jan 12 12:05:13 2012 From: kinsley.wong at oracle.com (Kinsley Wong) Date: Thu, 12 Jan 2012 12:05:13 -0800 Subject: API addition to SplitPane Message-ID: <4F0F3CF9.1010406@oracle.com> The Controls team would like to add the following new api below to SplitPane public static void resizableWithParent(Node node, Boolean value); public static Boolean isResizableWithParent(Node node); resizableWithParent(Node node, Boolean value) will set a Boolean property on the node. If the Boolean value is false the panel will not be resized when the SplitPane is resized. By default all panels in a SplitPane are resized when the window is resized. http://javafx-jira.kenai.com/browse/RT-18806 From richard.bair at oracle.com Thu Jan 12 12:16:50 2012 From: richard.bair at oracle.com (Richard Bair) Date: Thu, 12 Jan 2012 12:16:50 -0800 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: <4F0B4A0B.1090107@oracle.com> References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> <4F0B3228.9050204@oracle.com> <4F0B35CA.7070008@oracle.com> <4F0B44F9.2050007@oracle.com> <4F0B4A0B.1090107@oracle.com> Message-ID: Hi Steve, I know what you mean about wanting to have an API without error modes. In this case though I think it is a little restricting. I might for example want to provide different stylesheets for different platforms. On one platform I have the menu bar at the top in its "usual" place, but on another platform I might have the menu bar on the left or bottom. I might want to use CSS to toggle "use native please" vs. on another platform I would use CSS to just use emulated. Because setting stage.setMenuBar(node) would be a hard-coded call, I wouldn't have this flexibility to style it from CSS. I think it should be called "useSystemMenuBar" or "useNativeMenuBar" over "useGlobalMenuBar" -- just because we might want to allow you to use native menus on windows instead of emulated and they would be per-stage rather than "global" or application wide. Thanks Richard On Jan 9, 2012, at 12:11 PM, steve.x.northover at oracle.com wrote: > Having the property on MenuBar rather than the API on stage means that you can set the property on two menu bars at the same time and FX must choose one. There can't be two native menu bars at the same time on the same stage so why have API that allows it? Having an API in stage means that an application can easily query what the current menu bar is or whether there is a menu bar rather than performing the same search that FX does. > > I suppose that you could add API in stage later but that would mean two sets of API to do the same thing and the question about which one would win when they are mixed and matched. > > Steve > > On 09/01/2012 2:50 PM, Paru Somashekar wrote: >> On 1/9/12 10:45 AM, steve.x.northover at oracle.com wrote: >>> Hello! >>> >>> I believe there are problems with 1) and 2). First off, there is no way to mix and match. For example, suppose I want some shells to use the global menu bar and others to use a local menu bar. Further, application code may put trimmings around a menu bar and when the menu bar is "hoisted up", the window might look strange. >> The useGlobalMenuBar property on Menubar would not be a static property. So we could mix and match as the property is per MenuBar. >>> >>> If we are doing 3), I think a better approach would be for the application to decide whether it is using native menu bars or not by explicitly setting a menu bar on a stage. It makes sense because on platforms like Windows and GTK, there is a one-to-one mapping between stage and menu bar so why not make this explicit in the API? One of the problems with having API in Stage is that we don't want a reference to MenuBar. Why don't we define something like Stage.setMenyBarArea() or something like that to take a Node or another type instead? >> We were thinking that for this release, having a property on MenuBar which is set to false by default will ensure backwards and forwards compatibility (with minimal code changes for the application developer). If we want to change that to an API on Stage for a future release - it could be easily done without breaking compatibility. >> >> thanks, >> Paru. >> >>> >>> Steve >>> >>> On 09/01/2012 1:30 PM, Paru Somashekar wrote: >>>> Hello All, >>>> >>>> The following is the proposal (follow up from previous email by Jonathan) from UI Controls team towards an API to support native Mac OS menubar. >>>> >>>> 1) A new property useGlobalMenuBar would be added to MenuBar class whose initial value will be set to a default dictated by a property settable via CSS ( and set to false by default). Once again, for the first cut of this support in this release, this will be the only public API for native menubar support. >>>> >>>> 2) In the case when we have multiple Menubars specified on a stage, the first one wins and will be made global, i.e. provided the platform supports native integration and useGlobalMenuBar is set to true. >>>> >>>> 3) As mentioned in a previous email by Jonathan, we will have hooks to switch between stages when they become active and swap menu bars. In the case when we don?t find a Menubar, it will be nulled out. This is the path we are choosing for now and we realize that we can change this later with no backward compatibility issues. >>>> We are extending the same concept to the scenario where, when the last window closes; it will be nulled out as well and hence there is no concept of a default menubar. >>>> >>>> thanks, >>>> Paru. >>>> >>>> On 12/14/11 5:20 PM, Kevin Rushforth wrote: >>>>> An API on Stage to set a menu bar has a certain elegance, plus it gets around the backward compatibility and inconsistency problems that having "native" on by default would create. Unfortunately, as Rich pointed out earlier, it creates an unwanted dependency from a modularity point of view since Stage will be in the base module and should not depend on anything in controls. >>>>> >>>>> -- Kevin >>>>> >>>>> >>>>> steve.x.northover at oracle.com wrote: >>>>>> Hello all, >>>>>> >>>>>> How about an API in Stage where you set the menu bar for the stage? This is the menu that you wish to be native and the application very is clear about this. There can only be one real menu bar on Windows. Application code can ask for the menu bar and add items to it. If you have a property, application code needs to do the same search that FX does to determine if there is an active menu bar. >>>>>> >>>>>> Here is what I think about focus: On the Mac, when a stage gets focus, it sets the native menu bar. When it loses focus, it clears the native menu bar. Retaining the menu from the previous stage is unlikely to be the right thing to do as an application can have many different stages around and only the application can know whether the current menu bar applies to a stage. >>>>>> >>>>>> Steve >>>>>> >>>>>> On 14/12/2011 6:31 PM, Jonathan Giles wrote: >>>>>>> Hi All, >>>>>>> >>>>>>> Here's an update from the UI controls team as to how we see the native Mac OS menubar support working. Your thoughts are appreciated. >>>>>>> >>>>>>> After discussing it again today, we think that the approach suggested by Richard in an earlier email in this thread makes the best sense, in terms of modularity and code cleanliness. I'll explain this further shortly... >>>>>>> >>>>>>> The thinking is to add a new property to javafx.scene.control.MenuBar. We haven't settled on a name, but it's something along the lines of 'native', 'global', 'globalMenuBar', 'screenMenuBar', or 'applicationMenuBar'. Whatever property name we use, we'll expand it out to have the usual set*/get*/*property methods. This would be the only public API we end up adding for native menubar support. For the remainder of this email I refer to this property as 'native'. >>>>>>> >>>>>>> This property will by default be true, indicating that on platforms where we support native integration, it'll happen by default. >>>>>>> >>>>>>> On a platform that supports native integration, we'll find the 'first' MenuBar in the scene that has the 'native' property set to true. We can't guarantee that we'll find necessarily the physically top-most MenuBar as that is really a matter of how the scenegraph is laid out. Of course, this is only a problem in situations where the scene contains multiple MenuBars where 'native' is true in more than one of them, which we hope won't often be the case. If a Scene does have multiple MenuBars with 'native' set to true, the behaviour is undefined. If the wrong MenuBar is made native, you can help provide a hint by setting 'native' to false in the relevant place(s). >>>>>>> >>>>>>> We'll also hook into the Stage and listen to the relevant events, such that when a Stage gains focus, we'll switch in any native menubars found in the scene of that stage. If no relevant MenuBar is found, then we can either retain the MenuBar from the previous stage, or null it out. I'm going to assume the former is by far going to win this vote, but feel free to surprise me. >>>>>>> >>>>>>> Using this approach, developer code should be cleaner. Your user interface should position a MenuBar where it makes sense for your application, regardless of the operating system (normally at the very top of your scene). On platforms where native integration is supported, the JavaFX-rendered MenuBar will not be rendered (although it'll likely remain in the scenegraph as a no-op control). If the 'native' property changes, we'll flick between the native and JavaFX-rendered MenuBar as expected. This approach means there is no operating system dependent code in your user interface. >>>>>>> >>>>>>> As I mentioned - we're totally open to discussion on any of these points. Any thoughts? >>>>>>> >>>>>>> -- Jonathan >>>>>>> >>>>>>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: >>>>>>>> Hi all, >>>>>>>> >>>>>>>> One of the things we're planning to support in JavaFX 2.1 is the native Mac OS menubar. This email is intended primarily to discuss the API one expects to see to set a MenuBar in the native Mac OS menubar area. Your feedback is sought and will be very much appreciated. >>>>>>>> >>>>>>>> The current thinking is that Application feels like the right place to specify a global, application-wide javafx.scene.control.MenuBar on. It could be assumed that if a developer were to set this property, and the operating system upon which the end-user was running the JavaFX application was Mac OS, that the menubar will be displayed using the native Mac OS menubar. Of course, if a developer wants a cross-platform look and feel, they could just place the MenuBar in the stage as per usual and it would display as it currently does. This approach opens up a number of questions and issues: >>>>>>>> >>>>>>>> 1) What happens in the case of the end-user being on Windows? Is the Application.MenuBar ignored, or is it automagically added to the main Stage? (I would argue for totally ignoring it....but that leads to the next point). >>>>>>>> 2) This approach means there needs to be operating specific code in the UI to test whether a non-native MenuBar should be added (in the case of Windows, for example). This starts to clutter the UI code, and without careful consideration by the developer may result in needing to duplicate their MenuBar code. Is there a better approach? >>>>>>>> >>>>>>>> Another place to specify a MenuBar would be on Stage, rather than (or in addition to), Application. Having a MenuBar property on Stage would allow for the MenuBar to change based on the currently focused Stage - but I'm not certain this is desirable or even the expected behaviour of Mac OS. Therefore, I'm thinking that this is not likely to happen unless we hear otherwise. >>>>>>>> >>>>>>>> Like I said, we're at a very early exploration point in this process. The controls team is very keen to hear feedback from the community, as well as from the owners of the Application API, and the Mac OS experts on this list. >>>>>>>> >>>>>>>> Thanks, >>>>>>>> -- Jonathan >>>> >> From zonski at googlemail.com Thu Jan 12 12:22:07 2012 From: zonski at googlemail.com (Daniel Zwolenski) Date: Fri, 13 Jan 2012 06:22:07 +1000 Subject: API addition to SplitPane In-Reply-To: <4F0F3CF9.1010406@oracle.com> References: <4F0F3CF9.1010406@oracle.com> Message-ID: Just wondering if there's a case for making it a weighting system (not sure how useful this would be, but worth a quick thought): resizeWeight(Node node, float weight) Where the weight determines what percentage of the 'new' space the node gets (and how much it loses when shrinking) and a weight of zero is equivalent to your 'false'. Also, need to handle the case when the parent gets so small that all the 'resizable' nodes are already at zero. I assume in this case the nodes with resizable=false will actually start to shrink (alternatively the parent pane should not be allowed to go any smaller). On Fri, Jan 13, 2012 at 6:05 AM, Kinsley Wong wrote: > The Controls team would like to add the following new api below to > SplitPane > > public static void resizableWithParent(Node node, Boolean value); > public static Boolean isResizableWithParent(Node node); > > resizableWithParent(Node node, Boolean value) will set a Boolean property > on the node. If the Boolean value is false the panel will not be resized > when the SplitPane is resized. By default all panels in a SplitPane are > resized when the window is resized. > > http://javafx-jira.kenai.com/**browse/RT-18806 > > From zonski at googlemail.com Thu Jan 12 12:33:54 2012 From: zonski at googlemail.com (Daniel Zwolenski) Date: Fri, 13 Jan 2012 06:33:54 +1000 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> <4F0B3228.9050204@oracle.com> <4F0B35CA.7070008@oracle.com> <4F0B44F9.2050007@oracle.com> <4F0B4A0B.1090107@oracle.com> Message-ID: This one's not sitting too well with me. I don't like the idea of menu's jumping out and around magically. I'd personally rather see a more explicit API on handling 'system menu bars' and if it requires a little extra coding to get your Mac system bar working well on a Mac and your Windows one working on windows then that's not unreasonable in my opinion. I don't feel like there is actually a common system between the platforms so trying to make a system that just magically works for all feels dangerous (echoes of the well intentioned but ultimately disastrous AWT attempts to define a lowest common denominator native UI toolkit - emphasis on the word 'lowest'). Also, what happens when Mac OS Himalayan Meercat, Windows 9 or Linux Backroom Coder Edition comes out and they have decided that their system menu is now a rotating wheel with 3d shapes, sounds, and shooting lazers on it? It's not a feature I have used (or probably ever will use) enough to want to push the issue though, so just some sideline comments. On Fri, Jan 13, 2012 at 6:16 AM, Richard Bair wrote: > Hi Steve, > > I know what you mean about wanting to have an API without error modes. In > this case though I think it is a little restricting. I might for example > want to provide different stylesheets for different platforms. On one > platform I have the menu bar at the top in its "usual" place, but on > another platform I might have the menu bar on the left or bottom. I might > want to use CSS to toggle "use native please" vs. on another platform I > would use CSS to just use emulated. Because setting stage.setMenuBar(node) > would be a hard-coded call, I wouldn't have this flexibility to style it > from CSS. > > I think it should be called "useSystemMenuBar" or "useNativeMenuBar" over > "useGlobalMenuBar" -- just because we might want to allow you to use native > menus on windows instead of emulated and they would be per-stage rather > than "global" or application wide. > > Thanks > Richard > > On Jan 9, 2012, at 12:11 PM, steve.x.northover at oracle.com wrote: > > > Having the property on MenuBar rather than the API on stage means that > you can set the property on two menu bars at the same time and FX must > choose one. There can't be two native menu bars at the same time on the > same stage so why have API that allows it? Having an API in stage means > that an application can easily query what the current menu bar is or > whether there is a menu bar rather than performing the same search that FX > does. > > > > I suppose that you could add API in stage later but that would mean two > sets of API to do the same thing and the question about which one would win > when they are mixed and matched. > > > > Steve > > > > On 09/01/2012 2:50 PM, Paru Somashekar wrote: > >> On 1/9/12 10:45 AM, steve.x.northover at oracle.com wrote: > >>> Hello! > >>> > >>> I believe there are problems with 1) and 2). First off, there is no > way to mix and match. For example, suppose I want some shells to use the > global menu bar and others to use a local menu bar. Further, application > code may put trimmings around a menu bar and when the menu bar is "hoisted > up", the window might look strange. > >> The useGlobalMenuBar property on Menubar would not be a static > property. So we could mix and match as the property is per MenuBar. > >>> > >>> If we are doing 3), I think a better approach would be for the > application to decide whether it is using native menu bars or not by > explicitly setting a menu bar on a stage. It makes sense because on > platforms like Windows and GTK, there is a one-to-one mapping between stage > and menu bar so why not make this explicit in the API? One of the problems > with having API in Stage is that we don't want a reference to MenuBar. Why > don't we define something like Stage.setMenyBarArea() or something like > that to take a Node or another type instead? > >> We were thinking that for this release, having a property on MenuBar > which is set to false by default will ensure backwards and forwards > compatibility (with minimal code changes for the application developer). If > we want to change that to an API on Stage for a future release - it could > be easily done without breaking compatibility. > >> > >> thanks, > >> Paru. > >> > >>> > >>> Steve > >>> > >>> On 09/01/2012 1:30 PM, Paru Somashekar wrote: > >>>> Hello All, > >>>> > >>>> The following is the proposal (follow up from previous email by > Jonathan) from UI Controls team towards an API to support native Mac OS > menubar. > >>>> > >>>> 1) A new property useGlobalMenuBar would be added to MenuBar class > whose initial value will be set to a default dictated by a property > settable via CSS ( and set to false by default). Once again, for the first > cut of this support in this release, this will be the only public API for > native menubar support. > >>>> > >>>> 2) In the case when we have multiple Menubars specified on a stage, > the first one wins and will be made global, i.e. provided the platform > supports native integration and useGlobalMenuBar is set to true. > >>>> > >>>> 3) As mentioned in a previous email by Jonathan, we will have hooks > to switch between stages when they become active and swap menu bars. In the > case when we don?t find a Menubar, it will be nulled out. This is the path > we are choosing for now and we realize that we can change this later with > no backward compatibility issues. > >>>> We are extending the same concept to the scenario where, when the > last window closes; it will be nulled out as well and hence there is no > concept of a default menubar. > >>>> > >>>> thanks, > >>>> Paru. > >>>> > >>>> On 12/14/11 5:20 PM, Kevin Rushforth wrote: > >>>>> An API on Stage to set a menu bar has a certain elegance, plus it > gets around the backward compatibility and inconsistency problems that > having "native" on by default would create. Unfortunately, as Rich pointed > out earlier, it creates an unwanted dependency from a modularity point of > view since Stage will be in the base module and should not depend on > anything in controls. > >>>>> > >>>>> -- Kevin > >>>>> > >>>>> > >>>>> steve.x.northover at oracle.com wrote: > >>>>>> Hello all, > >>>>>> > >>>>>> How about an API in Stage where you set the menu bar for the stage? > This is the menu that you wish to be native and the application very is > clear about this. There can only be one real menu bar on Windows. > Application code can ask for the menu bar and add items to it. If you have > a property, application code needs to do the same search that FX does to > determine if there is an active menu bar. > >>>>>> > >>>>>> Here is what I think about focus: On the Mac, when a stage gets > focus, it sets the native menu bar. When it loses focus, it clears the > native menu bar. Retaining the menu from the previous stage is unlikely to > be the right thing to do as an application can have many different stages > around and only the application can know whether the current menu bar > applies to a stage. > >>>>>> > >>>>>> Steve > >>>>>> > >>>>>> On 14/12/2011 6:31 PM, Jonathan Giles wrote: > >>>>>>> Hi All, > >>>>>>> > >>>>>>> Here's an update from the UI controls team as to how we see the > native Mac OS menubar support working. Your thoughts are appreciated. > >>>>>>> > >>>>>>> After discussing it again today, we think that the approach > suggested by Richard in an earlier email in this thread makes the best > sense, in terms of modularity and code cleanliness. I'll explain this > further shortly... > >>>>>>> > >>>>>>> The thinking is to add a new property to > javafx.scene.control.MenuBar. We haven't settled on a name, but it's > something along the lines of 'native', 'global', 'globalMenuBar', > 'screenMenuBar', or 'applicationMenuBar'. Whatever property name we use, > we'll expand it out to have the usual set*/get*/*property methods. This > would be the only public API we end up adding for native menubar support. > For the remainder of this email I refer to this property as 'native'. > >>>>>>> > >>>>>>> This property will by default be true, indicating that on > platforms where we support native integration, it'll happen by default. > >>>>>>> > >>>>>>> On a platform that supports native integration, we'll find the > 'first' MenuBar in the scene that has the 'native' property set to true. We > can't guarantee that we'll find necessarily the physically top-most MenuBar > as that is really a matter of how the scenegraph is laid out. Of course, > this is only a problem in situations where the scene contains multiple > MenuBars where 'native' is true in more than one of them, which we hope > won't often be the case. If a Scene does have multiple MenuBars with > 'native' set to true, the behaviour is undefined. If the wrong MenuBar is > made native, you can help provide a hint by setting 'native' to false in > the relevant place(s). > >>>>>>> > >>>>>>> We'll also hook into the Stage and listen to the relevant events, > such that when a Stage gains focus, we'll switch in any native menubars > found in the scene of that stage. If no relevant MenuBar is found, then we > can either retain the MenuBar from the previous stage, or null it out. I'm > going to assume the former is by far going to win this vote, but feel free > to surprise me. > >>>>>>> > >>>>>>> Using this approach, developer code should be cleaner. Your user > interface should position a MenuBar where it makes sense for your > application, regardless of the operating system (normally at the very top > of your scene). On platforms where native integration is supported, the > JavaFX-rendered MenuBar will not be rendered (although it'll likely remain > in the scenegraph as a no-op control). If the 'native' property changes, > we'll flick between the native and JavaFX-rendered MenuBar as expected. > This approach means there is no operating system dependent code in your > user interface. > >>>>>>> > >>>>>>> As I mentioned - we're totally open to discussion on any of these > points. Any thoughts? > >>>>>>> > >>>>>>> -- Jonathan > >>>>>>> > >>>>>>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: > >>>>>>>> Hi all, > >>>>>>>> > >>>>>>>> One of the things we're planning to support in JavaFX 2.1 is the > native Mac OS menubar. This email is intended primarily to discuss the API > one expects to see to set a MenuBar in the native Mac OS menubar area. Your > feedback is sought and will be very much appreciated. > >>>>>>>> > >>>>>>>> The current thinking is that Application feels like the right > place to specify a global, application-wide javafx.scene.control.MenuBar > on. It could be assumed that if a developer were to set this property, and > the operating system upon which the end-user was running the JavaFX > application was Mac OS, that the menubar will be displayed using the native > Mac OS menubar. Of course, if a developer wants a cross-platform look and > feel, they could just place the MenuBar in the stage as per usual and it > would display as it currently does. This approach opens up a number of > questions and issues: > >>>>>>>> > >>>>>>>> 1) What happens in the case of the end-user being on Windows? Is > the Application.MenuBar ignored, or is it automagically added to the main > Stage? (I would argue for totally ignoring it....but that leads to the next > point). > >>>>>>>> 2) This approach means there needs to be operating specific code > in the UI to test whether a non-native MenuBar should be added (in the case > of Windows, for example). This starts to clutter the UI code, and without > careful consideration by the developer may result in needing to duplicate > their MenuBar code. Is there a better approach? > >>>>>>>> > >>>>>>>> Another place to specify a MenuBar would be on Stage, rather than > (or in addition to), Application. Having a MenuBar property on Stage would > allow for the MenuBar to change based on the currently focused Stage - but > I'm not certain this is desirable or even the expected behaviour of Mac OS. > Therefore, I'm thinking that this is not likely to happen unless we hear > otherwise. > >>>>>>>> > >>>>>>>> Like I said, we're at a very early exploration point in this > process. The controls team is very keen to hear feedback from the > community, as well as from the owners of the Application API, and the Mac > OS experts on this list. > >>>>>>>> > >>>>>>>> Thanks, > >>>>>>>> -- Jonathan > >>>> > >> > > From steve.x.northover at oracle.com Thu Jan 12 12:55:34 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Thu, 12 Jan 2012 15:55:34 -0500 Subject: Supporting the Mac OS menubar in JavaFX In-Reply-To: References: <4EE29218.4090305@oracle.com> <4EE931EA.8050903@oracle.com> <4EE93A78.8090803@oracle.com> <4EE94B5E.8080401@oracle.com> <4F0B3228.9050204@oracle.com> <4F0B35CA.7070008@oracle.com> <4F0B44F9.2050007@oracle.com> <4F0B4A0B.1090107@oracle.com> Message-ID: <4F0F48C6.7070607@oracle.com> What we are really talking about here is whether we are either: 1) distributing the native state to many potential different menu bars (yes, I know that a real application will only have one menu bar per stage, but the API allows the multiple to be specified and we must handle it) and allowing the application to position menu bars in the scene (with a "zero size" supplied for free due to the native property) or 2) having the native state in one place (no ambiguity) and having FX position the menu bar in a standard place (no "zero size" magic). Certainly, with no API on Stage, there are no decisions about where to place the menu bar when non-native and the API is called (ie. if we have Stage.setMenuBarArea(), then we need to write code in the non-native case for Stage in places that we may not yet support a native menu bar or may never support a native menu bar. I'm still not happy, but the CSS argument is a good one. I'm not happy, but let's go with the property and see how this one plays out. Steve On 12/01/2012 3:16 PM, Richard Bair wrote: > Hi Steve, > > I know what you mean about wanting to have an API without error modes. In this case though I think it is a little restricting. I might for example want to provide different stylesheets for different platforms. On one platform I have the menu bar at the top in its "usual" place, but on another platform I might have the menu bar on the left or bottom. I might want to use CSS to toggle "use native please" vs. on another platform I would use CSS to just use emulated. Because setting stage.setMenuBar(node) would be a hard-coded call, I wouldn't have this flexibility to style it from CSS. > > I think it should be called "useSystemMenuBar" or "useNativeMenuBar" over "useGlobalMenuBar" -- just because we might want to allow you to use native menus on windows instead of emulated and they would be per-stage rather than "global" or application wide. > > Thanks > Richard > > On Jan 9, 2012, at 12:11 PM, steve.x.northover at oracle.com wrote: > >> Having the property on MenuBar rather than the API on stage means that you can set the property on two menu bars at the same time and FX must choose one. There can't be two native menu bars at the same time on the same stage so why have API that allows it? Having an API in stage means that an application can easily query what the current menu bar is or whether there is a menu bar rather than performing the same search that FX does. >> >> I suppose that you could add API in stage later but that would mean two sets of API to do the same thing and the question about which one would win when they are mixed and matched. >> >> Steve >> >> On 09/01/2012 2:50 PM, Paru Somashekar wrote: >>> On 1/9/12 10:45 AM, steve.x.northover at oracle.com wrote: >>>> Hello! >>>> >>>> I believe there are problems with 1) and 2). First off, there is no way to mix and match. For example, suppose I want some shells to use the global menu bar and others to use a local menu bar. Further, application code may put trimmings around a menu bar and when the menu bar is "hoisted up", the window might look strange. >>> The useGlobalMenuBar property on Menubar would not be a static property. So we could mix and match as the property is per MenuBar. >>>> If we are doing 3), I think a better approach would be for the application to decide whether it is using native menu bars or not by explicitly setting a menu bar on a stage. It makes sense because on platforms like Windows and GTK, there is a one-to-one mapping between stage and menu bar so why not make this explicit in the API? One of the problems with having API in Stage is that we don't want a reference to MenuBar. Why don't we define something like Stage.setMenyBarArea() or something like that to take a Node or another type instead? >>> We were thinking that for this release, having a property on MenuBar which is set to false by default will ensure backwards and forwards compatibility (with minimal code changes for the application developer). If we want to change that to an API on Stage for a future release - it could be easily done without breaking compatibility. >>> >>> thanks, >>> Paru. >>> >>>> Steve >>>> >>>> On 09/01/2012 1:30 PM, Paru Somashekar wrote: >>>>> Hello All, >>>>> >>>>> The following is the proposal (follow up from previous email by Jonathan) from UI Controls team towards an API to support native Mac OS menubar. >>>>> >>>>> 1) A new property useGlobalMenuBar would be added to MenuBar class whose initial value will be set to a default dictated by a property settable via CSS ( and set to false by default). Once again, for the first cut of this support in this release, this will be the only public API for native menubar support. >>>>> >>>>> 2) In the case when we have multiple Menubars specified on a stage, the first one wins and will be made global, i.e. provided the platform supports native integration and useGlobalMenuBar is set to true. >>>>> >>>>> 3) As mentioned in a previous email by Jonathan, we will have hooks to switch between stages when they become active and swap menu bars. In the case when we don?t find a Menubar, it will be nulled out. This is the path we are choosing for now and we realize that we can change this later with no backward compatibility issues. >>>>> We are extending the same concept to the scenario where, when the last window closes; it will be nulled out as well and hence there is no concept of a default menubar. >>>>> >>>>> thanks, >>>>> Paru. >>>>> >>>>> On 12/14/11 5:20 PM, Kevin Rushforth wrote: >>>>>> An API on Stage to set a menu bar has a certain elegance, plus it gets around the backward compatibility and inconsistency problems that having "native" on by default would create. Unfortunately, as Rich pointed out earlier, it creates an unwanted dependency from a modularity point of view since Stage will be in the base module and should not depend on anything in controls. >>>>>> >>>>>> -- Kevin >>>>>> >>>>>> >>>>>> steve.x.northover at oracle.com wrote: >>>>>>> Hello all, >>>>>>> >>>>>>> How about an API in Stage where you set the menu bar for the stage? This is the menu that you wish to be native and the application very is clear about this. There can only be one real menu bar on Windows. Application code can ask for the menu bar and add items to it. If you have a property, application code needs to do the same search that FX does to determine if there is an active menu bar. >>>>>>> >>>>>>> Here is what I think about focus: On the Mac, when a stage gets focus, it sets the native menu bar. When it loses focus, it clears the native menu bar. Retaining the menu from the previous stage is unlikely to be the right thing to do as an application can have many different stages around and only the application can know whether the current menu bar applies to a stage. >>>>>>> >>>>>>> Steve >>>>>>> >>>>>>> On 14/12/2011 6:31 PM, Jonathan Giles wrote: >>>>>>>> Hi All, >>>>>>>> >>>>>>>> Here's an update from the UI controls team as to how we see the native Mac OS menubar support working. Your thoughts are appreciated. >>>>>>>> >>>>>>>> After discussing it again today, we think that the approach suggested by Richard in an earlier email in this thread makes the best sense, in terms of modularity and code cleanliness. I'll explain this further shortly... >>>>>>>> >>>>>>>> The thinking is to add a new property to javafx.scene.control.MenuBar. We haven't settled on a name, but it's something along the lines of 'native', 'global', 'globalMenuBar', 'screenMenuBar', or 'applicationMenuBar'. Whatever property name we use, we'll expand it out to have the usual set*/get*/*property methods. This would be the only public API we end up adding for native menubar support. For the remainder of this email I refer to this property as 'native'. >>>>>>>> >>>>>>>> This property will by default be true, indicating that on platforms where we support native integration, it'll happen by default. >>>>>>>> >>>>>>>> On a platform that supports native integration, we'll find the 'first' MenuBar in the scene that has the 'native' property set to true. We can't guarantee that we'll find necessarily the physically top-most MenuBar as that is really a matter of how the scenegraph is laid out. Of course, this is only a problem in situations where the scene contains multiple MenuBars where 'native' is true in more than one of them, which we hope won't often be the case. If a Scene does have multiple MenuBars with 'native' set to true, the behaviour is undefined. If the wrong MenuBar is made native, you can help provide a hint by setting 'native' to false in the relevant place(s). >>>>>>>> >>>>>>>> We'll also hook into the Stage and listen to the relevant events, such that when a Stage gains focus, we'll switch in any native menubars found in the scene of that stage. If no relevant MenuBar is found, then we can either retain the MenuBar from the previous stage, or null it out. I'm going to assume the former is by far going to win this vote, but feel free to surprise me. >>>>>>>> >>>>>>>> Using this approach, developer code should be cleaner. Your user interface should position a MenuBar where it makes sense for your application, regardless of the operating system (normally at the very top of your scene). On platforms where native integration is supported, the JavaFX-rendered MenuBar will not be rendered (although it'll likely remain in the scenegraph as a no-op control). If the 'native' property changes, we'll flick between the native and JavaFX-rendered MenuBar as expected. This approach means there is no operating system dependent code in your user interface. >>>>>>>> >>>>>>>> As I mentioned - we're totally open to discussion on any of these points. Any thoughts? >>>>>>>> >>>>>>>> -- Jonathan >>>>>>>> >>>>>>>> On 10/12/2011 8:56 a.m., Jonathan Giles wrote: >>>>>>>>> Hi all, >>>>>>>>> >>>>>>>>> One of the things we're planning to support in JavaFX 2.1 is the native Mac OS menubar. This email is intended primarily to discuss the API one expects to see to set a MenuBar in the native Mac OS menubar area. Your feedback is sought and will be very much appreciated. >>>>>>>>> >>>>>>>>> The current thinking is that Application feels like the right place to specify a global, application-wide javafx.scene.control.MenuBar on. It could be assumed that if a developer were to set this property, and the operating system upon which the end-user was running the JavaFX application was Mac OS, that the menubar will be displayed using the native Mac OS menubar. Of course, if a developer wants a cross-platform look and feel, they could just place the MenuBar in the stage as per usual and it would display as it currently does. This approach opens up a number of questions and issues: >>>>>>>>> >>>>>>>>> 1) What happens in the case of the end-user being on Windows? Is the Application.MenuBar ignored, or is it automagically added to the main Stage? (I would argue for totally ignoring it....but that leads to the next point). >>>>>>>>> 2) This approach means there needs to be operating specific code in the UI to test whether a non-native MenuBar should be added (in the case of Windows, for example). This starts to clutter the UI code, and without careful consideration by the developer may result in needing to duplicate their MenuBar code. Is there a better approach? >>>>>>>>> >>>>>>>>> Another place to specify a MenuBar would be on Stage, rather than (or in addition to), Application. Having a MenuBar property on Stage would allow for the MenuBar to change based on the currently focused Stage - but I'm not certain this is desirable or even the expected behaviour of Mac OS. Therefore, I'm thinking that this is not likely to happen unless we hear otherwise. >>>>>>>>> >>>>>>>>> Like I said, we're at a very early exploration point in this process. The controls team is very keen to hear feedback from the community, as well as from the owners of the Application API, and the Mac OS experts on this list. >>>>>>>>> >>>>>>>>> Thanks, >>>>>>>>> -- Jonathan From hang.vo at oracle.com Thu Jan 12 14:52:58 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 12 Jan 2012 22:52:58 +0000 Subject: hg: openjfx/2.1/master/rt: 4 new changesets Message-ID: <20120112225300.676FD47949@hg.openjdk.java.net> Changeset: 056d61a3b3b4 Author: jgiles Date: 2012-01-13 08:10 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/056d61a3b3b4 RT-18904: Controls.ListView-Rotate performance dropped up to 0 fps in fx2.1-b08 ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java Changeset: 83b0ae82038b Author: jgiles Date: 2012-01-13 10:19 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/83b0ae82038b RT-18904: Performance optimisation to ReadOnlyUnbackedList.subList ! javafx-ui-controls/src/com/sun/javafx/scene/control/ReadOnlyUnbackedObservableList.java Changeset: e4d7464ce84a Author: jgiles Date: 2012-01-13 10:26 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e4d7464ce84a [TEST ONLY] RT-18920: UX test cases for keyboard navigation in TreeView ! javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java Changeset: 5771e7157e47 Author: jgiles Date: 2012-01-13 10:28 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/5771e7157e47 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From jonathan.giles at oracle.com Thu Jan 12 16:15:01 2012 From: jonathan.giles at oracle.com (Jonathan Giles) Date: Fri, 13 Jan 2012 13:15:01 +1300 Subject: [Review Request] Adding a TextField alignment property Message-ID: <4F0F7785.6050905@oracle.com> Hi all, http://javafx-jira.kenai.com/browse/RT-18410 This review request relates to adding an alignment property to TextField to allow for developers to specify the alignment of text. The API is the usual JavaFX beans API: getAlignment(), setAlignment(Pos) and alignmentProperty(). In addition, an -fx-alignment property is exposed via CSS to allow for setting alignment in CSS. The default value will be Pos.CENTER_LEFT so as to not affect current users of TextField (plus it's the sane default anyway!) A patch for this API change can be seen at http://javafx-jira.kenai.com/browse/RT-18410 It is our intention to include this API in JavaFX 2.1, so with feature freeze nearing feedback would be appreciated sooner rather than later. Thanks, Jonathan From lubomir.nerad at oracle.com Fri Jan 13 07:12:01 2012 From: lubomir.nerad at oracle.com (Lubomir Nerad) Date: Fri, 13 Jan 2012 16:12:01 +0100 Subject: Null pointer checks in event handler / filter registration methods Message-ID: <4F1049C1.30302@oracle.com> Hi, I am working on http://javafx-jira.kenai.com/browse/RT-18865 (Adding null event handlers is allowed - later leads to NPE). To fix it I want to add null checks (with NPE throws) for event type and event handler or filter to the addEventHandler, removeEventHandler, addEventFilter and removeEventFilter methods of Node, Scene, Window, Task, Service, MenuItem, TableColumn and TreeItem. Passing null event type or event handler / filter to these methods clearly indicates programmer's error and so should be reported early. Failure to do that may result in an exception later in program execution which cause is then hard to guess. I don't know whether this change requires an explicit approval, but previously javadoc for these methods didn't specify possible NPE and so the API behavior will change in this respect. Thanks, Lubo From richard.bair at oracle.com Fri Jan 13 07:21:50 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 13 Jan 2012 07:21:50 -0800 Subject: Null pointer checks in event handler / filter registration methods In-Reply-To: <4F1049C1.30302@oracle.com> References: <4F1049C1.30302@oracle.com> Message-ID: I think you are fine, sounds like a bug to me as opposed to a change in intent. I'm assuming you will update the docs to indicate null is not allowed. If somebody binds onMouseClicked to null, that should not thrown exception but just unregister any previous handler and not register a new one, right? Thanks Richard On Jan 13, 2012, at 7:12 AM, Lubomir Nerad wrote: > Hi, > > I am working on http://javafx-jira.kenai.com/browse/RT-18865 (Adding null event handlers is allowed - later leads to NPE). To fix it I want to add null checks (with NPE throws) for event type and event handler or filter to the addEventHandler, removeEventHandler, addEventFilter and removeEventFilter methods of Node, Scene, Window, Task, Service, MenuItem, TableColumn and TreeItem. Passing null event type or event handler / filter to these methods clearly indicates programmer's error and so should be reported early. Failure to do that may result in an exception later in program execution which cause is then hard to guess. > > I don't know whether this change requires an explicit approval, but previously javadoc for these methods didn't specify possible NPE and so the API behavior will change in this respect. > > Thanks, > Lubo > From lubomir.nerad at oracle.com Fri Jan 13 07:29:54 2012 From: lubomir.nerad at oracle.com (Lubomir Nerad) Date: Fri, 13 Jan 2012 16:29:54 +0100 Subject: Null pointer checks in event handler / filter registration methods In-Reply-To: References: <4F1049C1.30302@oracle.com> Message-ID: <4F104DF2.3030501@oracle.com> Right, null event handler value will remain valid for on properties. On 13.1.2012 16:21, Richard Bair wrote: > I think you are fine, sounds like a bug to me as opposed to a change in intent. I'm assuming you will update the docs to indicate null is not allowed. If somebody binds onMouseClicked to null, that should not thrown exception but just unregister any previous handler and not register a new one, right? > > Thanks > Richard > > > > On Jan 13, 2012, at 7:12 AM, Lubomir Nerad wrote: > >> Hi, >> >> I am working on http://javafx-jira.kenai.com/browse/RT-18865 (Adding null event handlers is allowed - later leads to NPE). To fix it I want to add null checks (with NPE throws) for event type and event handler or filter to the addEventHandler, removeEventHandler, addEventFilter and removeEventFilter methods of Node, Scene, Window, Task, Service, MenuItem, TableColumn and TreeItem. Passing null event type or event handler / filter to these methods clearly indicates programmer's error and so should be reported early. Failure to do that may result in an exception later in program execution which cause is then hard to guess. >> >> I don't know whether this change requires an explicit approval, but previously javadoc for these methods didn't specify possible NPE and so the API behavior will change in this respect. >> >> Thanks, >> Lubo >> From richard.bair at oracle.com Fri Jan 13 08:53:20 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 13 Jan 2012 08:53:20 -0800 Subject: [Review Request] Adding a TextField alignment property In-Reply-To: <4F0F7785.6050905@oracle.com> References: <4F0F7785.6050905@oracle.com> Message-ID: Looks good! On Jan 12, 2012, at 4:15 PM, Jonathan Giles wrote: > Hi all, > > http://javafx-jira.kenai.com/browse/RT-18410 > > This review request relates to adding an alignment property to TextField to allow for developers to specify the alignment of text. The API is the usual JavaFX beans API: getAlignment(), setAlignment(Pos) and alignmentProperty(). In addition, an -fx-alignment property is exposed via CSS to allow for setting alignment in CSS. > > The default value will be Pos.CENTER_LEFT so as to not affect current users of TextField (plus it's the sane default anyway!) > > A patch for this API change can be seen at http://javafx-jira.kenai.com/browse/RT-18410 > > It is our intention to include this API in JavaFX 2.1, so with feature freeze nearing feedback would be appreciated sooner rather than later. > > Thanks, > Jonathan From richard.bair at oracle.com Fri Jan 13 10:20:41 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 13 Jan 2012 10:20:41 -0800 Subject: Feature Freeze is Imminent! Message-ID: Hi All, I wanted to post a short message on our current state of development, terminology, and what is coming next. First, the feature freeze for 2.1 is coming next week. This means that the conversation is going to turn from feature work (new APIs and features) to bug fixing. "Feature freeze", in our terminology, means that the feature work is completed on the development side. The only way to change an API after mid next week will be through some process that involves sign-off from the stakeholders: dev, management, testing, etc. It isn't impossible, but I'd really like to see us make the transition to full-on bug fixing. We've got another release (2.2) coming later this year so it isn't the end of the world, but paying down our technical debt is a big deal right now so we don't get swamped later on. Right now, any committer can fix bugs freely. Just get your normal code reviews or whatever process your team is following and off you go. In a couple months we'll clamp down and start "bug courts". In our old internal process, the way this would work is that we'd get dev, sqe, management, etc into a room (or on the phone) and if you want to put back a bug (any bug) fix you have to first justify it. There was a wiki to keep track of approval / no approval and everything. We also kept track in JIRA. We haven't nailed down the openjfx process, but I assume what we'll do is we'll do it via JIRA. We might still get together in a room and go through all the JIRA issues and discuss and then update the JIRA issue with our votes. Since at the moment there are no non-oracle committers, you will need an Oracle proxy anyway, so that's OK. I *hope* in the next few months some people will be advancing to the committer stage (after having submitted many bug patches, hint hint :-)) and then we'll want to get a skype going or a live chat or something. I'm not sure, we'll have to resolve that part of the process so that everybody is operating on equal footing. Once we've hit high resistance, you know we're getting close to a release. The final phase is "code freeze" where, after that, no bug fixes are going to happen unless they are absolute show stoppers. When we hit high resistance, we'll also open up new repos for 2.2. So during high resistance the bug fix rate for 2.1 goes down, the new feature work for 2.2 starts up again, and off we go for another release. There is a JIRA dashboard I've created (well, cribbed more like, Brian Beck did all the work) called "2.1 OpenJFX Dashboard". You can put this on your JIRA home page by clicking "Dashboards | Manage Dashboards" in the top left corner and then doing a search by name for "2.1 OpenJFX Dashboard". Then click the start to make it a favorite. Then in your home page you will have a tab for it. This page has the following portlets: This shows all of the issues which are unresolved and targeted at 2.1. It is broken down by functional area and priority. At the end of the release, any P1-P3 issues that haven't been fixed will have to go through a "deferral process". However since we don't want to see our bug backlog grow out of bounds, I'd really, *really* like to see us resolve at least this many issues. Of course during the next couple months the bug inflow rate will continue, so in the end we will fix quite a few more issues than 298 and might still have some issues which need deferral. But I hope we can at least not get in any further technical debt with regards to our bug backlog. As Brian Beck would say, you can live in hope or die in despair :-) The "PRD" stands for "Product Request Document" (or something like that) and is basically the product management's ask for what major features should go into a release. This chart tracks the progress of the completion of these features. This should be 0 next week! Any feature that isn't complete next week will have some 'splainin to do. Since mac support is one of the big things for 2.1, we've got a special table listing those bugs that are impacting the mac work specifically. And for all the haters who don't believe we're working on Linux, here you go. A "New" Bug is one that hasn't been triaged. During the triage process, we validate the priority, owner, targeted release, etc. There are always going to be "new" bugs as every new bug starts of life as "new" and, darn it, bugs get filed. Anyway, this chart is used to indicate which areas are in need of cleaning out their new bugs. This is the chart I have burned into my retina. Basically it is keeping track of the bug inflow vs. the fix rate. As you can see it has been narrowing since the new year, but what we really want to see here is a whole lot of green! I am making and will be making an impassioned plea for new contributors to put your shoulder to the wheel and help us tackle these bugs! I know each of you have your own pet bugs that you've filed and need fixed, and I'll be outlining the bug fix process and making it as lightweight as possible. Given the sources for controls are already out, that's a great place to start with fixing! The path to becoming a full-fledged committer on OpenJFX starts with submitting a series of patches! I would love to start inducting some of you guys who have been making valuable contributions to the discussions. This is a tattle-tale chart. It tells us which of us are naughty and have bugs in the "new" state for too long. These are problematic because hiding in one of these "new" bugs might be a critical bug or a blocker that we will get stung by later. So if you have your name on this chart, you'd better get crackin' and get these bugs out of the new state! Finally, here is another trouble indicator -- a blocker should never, ever, be targeted at another release. If it is targeted at another release, then it isn't really a blocker, is it? That's what this chart attempts to capture. I hope this helps give you the information you need to start hacking on this with us and actually get some of your code into JavaFX! Thanks Richard From richard.bair at oracle.com Fri Jan 13 10:32:50 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 13 Jan 2012 10:32:50 -0800 Subject: API addition to SplitPane In-Reply-To: References: <4F0F3CF9.1010406@oracle.com> Message-ID: <98C22A32-4DCD-440B-BE1C-D2F5FD66D712@oracle.com> On Jan 12, 2012, at 12:22 PM, Daniel Zwolenski wrote: > Just wondering if there's a case for making it a weighting system (not sure > how useful this would be, but worth a quick thought): > > resizeWeight(Node node, float weight) > > Where the weight determines what percentage of the 'new' space the node > gets (and how much it loses when shrinking) and a weight of zero is > equivalent to your 'false'. My initial reaction was the same, although I would have used the "ALWAYS", "SOMETIMES", "NEVER" system used by GridPane. Jasper, can you articulate why you think the boolean is preferable? > Also, need to handle the case when the parent gets so small that all the > 'resizable' nodes are already at zero. I assume in this case the nodes with > resizable=false will actually start to shrink (alternatively the parent > pane should not be allowed to go any smaller). Ya, basically this is a system of layout requests "Please, I'd prefer not to be resized" but if it has to, it will. Note that a setMinWidth etc is going onto Stage in a few days here, so at least you will be able to create a window where the user cannot shrink it past a certain point. Richard From richard.bair at oracle.com Fri Jan 13 11:00:27 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 13 Jan 2012 11:00:27 -0800 Subject: Feature Freeze is Imminent! In-Reply-To: References: Message-ID: Well, drat. None of the pictures made it, and the darn WIKI won't let me post a new page, keeps saying me session has timed out. I guess you'll have to go load the dashboard in JIRA and figure out which missing images go with each missing portlet :-) Richard On Jan 13, 2012, at 10:20 AM, Richard Bair wrote: > Hi All, > > I wanted to post a short message on our current state of development, terminology, and what is coming next. First, the feature freeze for 2.1 is coming next week. This means that the conversation is going to turn from feature work (new APIs and features) to bug fixing. > > "Feature freeze", in our terminology, means that the feature work is completed on the development side. The only way to change an API after mid next week will be through some process that involves sign-off from the stakeholders: dev, management, testing, etc. It isn't impossible, but I'd really like to see us make the transition to full-on bug fixing. We've got another release (2.2) coming later this year so it isn't the end of the world, but paying down our technical debt is a big deal right now so we don't get swamped later on. > > Right now, any committer can fix bugs freely. Just get your normal code reviews or whatever process your team is following and off you go. In a couple months we'll clamp down and start "bug courts". In our old internal process, the way this would work is that we'd get dev, sqe, management, etc into a room (or on the phone) and if you want to put back a bug (any bug) fix you have to first justify it. There was a wiki to keep track of approval / no approval and everything. We also kept track in JIRA. We haven't nailed down the openjfx process, but I assume what we'll do is we'll do it via JIRA. We might still get together in a room and go through all the JIRA issues and discuss and then update the JIRA issue with our votes. Since at the moment there are no non-oracle committers, you will need an Oracle proxy anyway, so that's OK. I *hope* in the next few months some people will be advancing to the committer stage (after having submitted many bug patches, hint hint :-)) and then we'll want to get a skype going or a live chat or something. I'm not sure, we'll have to resolve that part of the process so that everybody is operating on equal footing. > > Once we've hit high resistance, you know we're getting close to a release. The final phase is "code freeze" where, after that, no bug fixes are going to happen unless they are absolute show stoppers. > > When we hit high resistance, we'll also open up new repos for 2.2. So during high resistance the bug fix rate for 2.1 goes down, the new feature work for 2.2 starts up again, and off we go for another release. > > There is a JIRA dashboard I've created (well, cribbed more like, Brian Beck did all the work) called "2.1 OpenJFX Dashboard". You can put this on your JIRA home page by clicking "Dashboards | Manage Dashboards" in the top left corner and then doing a search by name for "2.1 OpenJFX Dashboard". Then click the start to make it a favorite. Then in your home page you will have a tab for it. > > This page has the following portlets: > > > > This shows all of the issues which are unresolved and targeted at 2.1. It is broken down by functional area and priority. At the end of the release, any P1-P3 issues that haven't been fixed will have to go through a "deferral process". However since we don't want to see our bug backlog grow out of bounds, I'd really, *really* like to see us resolve at least this many issues. Of course during the next couple months the bug inflow rate will continue, so in the end we will fix quite a few more issues than 298 and might still have some issues which need deferral. But I hope we can at least not get in any further technical debt with regards to our bug backlog. > > As Brian Beck would say, you can live in hope or die in despair :-) > > > > The "PRD" stands for "Product Request Document" (or something like that) and is basically the product management's ask for what major features should go into a release. This chart tracks the progress of the completion of these features. This should be 0 next week! Any feature that isn't complete next week will have some 'splainin to do. > > > > Since mac support is one of the big things for 2.1, we've got a special table listing those bugs that are impacting the mac work specifically. > > > > And for all the haters who don't believe we're working on Linux, here you go. > > > > A "New" Bug is one that hasn't been triaged. During the triage process, we validate the priority, owner, targeted release, etc. There are always going to be "new" bugs as every new bug starts of life as "new" and, darn it, bugs get filed. Anyway, this chart is used to indicate which areas are in need of cleaning out their new bugs. > > > > This is the chart I have burned into my retina. Basically it is keeping track of the bug inflow vs. the fix rate. As you can see it has been narrowing since the new year, but what we really want to see here is a whole lot of green! I am making and will be making an impassioned plea for new contributors to put your shoulder to the wheel and help us tackle these bugs! I know each of you have your own pet bugs that you've filed and need fixed, and I'll be outlining the bug fix process and making it as lightweight as possible. Given the sources for controls are already out, that's a great place to start with fixing! > > The path to becoming a full-fledged committer on OpenJFX starts with submitting a series of patches! I would love to start inducting some of you guys who have been making valuable contributions to the discussions. > > > > This is a tattle-tale chart. It tells us which of us are naughty and have bugs in the "new" state for too long. These are problematic because hiding in one of these "new" bugs might be a critical bug or a blocker that we will get stung by later. So if you have your name on this chart, you'd better get crackin' and get these bugs out of the new state! > > > > Finally, here is another trouble indicator -- a blocker should never, ever, be targeted at another release. If it is targeted at another release, then it isn't really a blocker, is it? That's what this chart attempts to capture. > > I hope this helps give you the information you need to start hacking on this with us and actually get some of your code into JavaFX! > > Thanks > Richard From hang.vo at oracle.com Fri Jan 13 12:33:50 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 13 Jan 2012 20:33:50 +0000 Subject: hg: openjfx/2.1/master/rt: Fixed: RT-18410: Add text alignment property to TextField Message-ID: <20120113203350.B082347965@hg.openjdk.java.net> Changeset: 75ca7cbe81d6 Author: leifs Date: 2012-01-13 12:22 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/75ca7cbe81d6 Fixed: RT-18410: Add text alignment property to TextField ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java ! javafx-ui-controls/src/javafx/scene/control/TextField.java From hang.vo at oracle.com Fri Jan 13 14:42:37 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 13 Jan 2012 22:42:37 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120113224238.0E7604796F@hg.openjdk.java.net> Changeset: 3d82c6b24da7 Author: jgiles Date: 2012-01-14 10:41 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3d82c6b24da7 RT-18920: UX test cases for keyboard navigation in TreeView ! javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java Changeset: a57888137e33 Author: jgiles Date: 2012-01-14 11:07 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/a57888137e33 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From kevin.rushforth at oracle.com Fri Jan 13 15:54:11 2012 From: kevin.rushforth at oracle.com (kevin.rushforth at oracle.com) Date: Fri, 13 Jan 2012 23:54:11 +0000 Subject: hg: openjfx/2.1/master: RT-18421: Build files in openjfx/2.1/master have the wrong copyright Message-ID: <20120113235412.0AD3347970@hg.openjdk.java.net> Changeset: 5a537ec76c6e Author: kcr Date: 2012-01-13 15:52 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rev/5a537ec76c6e RT-18421: Build files in openjfx/2.1/master have the wrong copyright Reviewed by: jgiles ! build-defs.xml ! build-src/build-cache.xml ! build-src/build-component-template.xml ! build-src/build-components.xml ! build-src/build-copy.xml ! build-src/build-environment.xml ! build-src/build-importer.xml ! build-src/build-inventory.xml ! build-src/build-invoice.xml ! build-src/build-jar-importer.xml ! build-src/build-linux-defs.xml ! build-src/build-macosx-defs.xml ! build-src/build-os-arch.xml ! build-src/build-perf.xml ! build-src/build-sdk-rt.xml ! build-src/build-solaris-defs.xml ! build-src/build-windows-defs.xml ! build-src/build-zips.xml ! build-src/genVSproperties.bat From kevin.rushforth at oracle.com Fri Jan 13 16:13:53 2012 From: kevin.rushforth at oracle.com (kevin.rushforth at oracle.com) Date: Sat, 14 Jan 2012 00:13:53 +0000 Subject: hg: openjfx/2.1: DUMMY REPOSITORY Message-ID: <20120114001353.EB1C547971@hg.openjdk.java.net> Changeset: 94e79b84a3fd Author: kcr Date: 2012-01-13 16:13 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/rev/94e79b84a3fd DUMMY REPOSITORY + README From hang.vo at oracle.com Sun Jan 15 16:13:27 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 16 Jan 2012 00:13:27 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120116001328.9275E4797A@hg.openjdk.java.net> Changeset: 9c8cb471b9a3 Author: jgiles Date: 2012-01-16 12:42 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/9c8cb471b9a3 Fixing a number of build issues related to incorrect copying of files ! javafx-beans-dt/project.properties ! javafx-concurrent/build.xml ! javafx-concurrent/project.properties ! javafx-designtime/project.properties Changeset: 76c936e4f685 Author: jgiles Date: 2012-01-16 13:08 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/76c936e4f685 Adding a rt/build.xml file that simplifies building the openjfx code. When run with 'ant' (i.e. no specified target), it generates an openjfxrt.jar file in the rt/dist folder. The only requirement is that the environment variable JFXRT_HOME is set to point to the lib folder where the jfxrt.jar file exists. For example, on my machine in Windows I have set the JFXRT_HOME environment variable to C:\programming\netbeans\Java-FX\artifacts\sdk\rt\lib + build.xml From hang.vo at oracle.com Sun Jan 15 16:42:51 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 16 Jan 2012 00:42:51 +0000 Subject: hg: openjfx/2.1/master/rt: Applying a few further build fixes that I didn't include in the last repo push. Message-ID: <20120116004251.D9CB74797B@hg.openjdk.java.net> Changeset: 11dc6a01620e Author: jgiles Date: 2012-01-16 13:37 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/11dc6a01620e Applying a few further build fixes that I didn't include in the last repo push. ! build-defs.xml ! build.xml ! javafx-ui-controls/nbproject/project.xml From hang.vo at oracle.com Sun Jan 15 16:52:40 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 16 Jan 2012 00:52:40 +0000 Subject: hg: openjfx/2.1/master/rt: Temporarily commenting out propertycopy that breaks openjfx build. Message-ID: <20120116005240.BD3304797C@hg.openjdk.java.net> Changeset: 3723daa03e05 Author: jgiles Date: 2012-01-16 13:45 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3723daa03e05 Temporarily commenting out propertycopy that breaks openjfx build. ! build-defs.xml From hang.vo at oracle.com Sun Jan 15 18:42:46 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 16 Jan 2012 02:42:46 +0000 Subject: hg: openjfx/2.1/master/rt: Making rt/dist directory if it doesn't already exist Message-ID: <20120116024247.2BEAC4797E@hg.openjdk.java.net> Changeset: f7a12e20b2ba Author: jgiles Date: 2012-01-16 15:33 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/f7a12e20b2ba Making rt/dist directory if it doesn't already exist ! build.xml From hang.vo at oracle.com Mon Jan 16 14:52:08 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 16 Jan 2012 22:52:08 +0000 Subject: hg: openjfx/2.1/master/rt: Further work on RT-18904: Controls.ListView-Rotate performance dropped up to 0 fps in fx2.1-b08. Improving the event being fired when selection is shifted to not include -1 values, and to short-circuit earlier if not selection shifting is required. Message-ID: <20120116225208.D6DA34798F@hg.openjdk.java.net> Changeset: 8181c2557b4c Author: jgiles Date: 2012-01-17 11:43 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/8181c2557b4c Further work on RT-18904: Controls.ListView-Rotate performance dropped up to 0 fps in fx2.1-b08. Improving the event being fired when selection is shifted to not include -1 values, and to short-circuit earlier if not selection shifting is required. ! javafx-ui-controls/src/javafx/scene/control/MultipleSelectionModelBase.java From hang.vo at oracle.com Mon Jan 16 18:32:56 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 17 Jan 2012 02:32:56 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18786: TableView.edit(int, tableColumn) method is not working in the JavaFx 2.1 b07 Message-ID: <20120117023256.E728A47990@hg.openjdk.java.net> Changeset: 6beb94343169 Author: jgiles Date: 2012-01-17 15:22 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6beb94343169 RT-18786: TableView.edit(int,tableColumn) method is not working in the JavaFx 2.1 b07 ! javafx-ui-controls/src/javafx/scene/control/TableCell.java From hang.vo at oracle.com Mon Jan 16 21:32:54 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 17 Jan 2012 05:32:54 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18855 SplitPane dividers positioning issue Message-ID: <20120117053255.3CDDE47991@hg.openjdk.java.net> Changeset: e3a4425e79e6 Author: Kinsley Wong Date: 2012-01-16 21:23 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e3a4425e79e6 RT-18855 SplitPane dividers positioning issue ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/SplitPaneSkin.java From tbee at tbee.org Tue Jan 17 05:35:01 2012 From: tbee at tbee.org (Tom Eugelink) Date: Tue, 17 Jan 2012 14:35:01 +0100 Subject: endless loop on property sync code Message-ID: <4F157905.1050702@tbee.org> In an attempt to make some code "cheaper" I decided it could be a good idea to redundantly store the index next to the value it pointed to. Since this could save a fairly costly "getIndex" call. So I ended up with two properties; one public value and one private index. Basically both denote the same value, so I needed to add sychronisation code: // react to changes of the value this.valueObjectProperty.addListener(new ChangeListener() { @Override public void changed(ObservableValue property, T oldValue, T newValue) { // get the value of the new index Object lIdx = getDataProvider().getIndex(newValue); if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); // set the value indexObjectProperty.setValue(lIdx); } }); // react to changes of the index this.indexObjectProperty.addListener(new ChangeListener() { @Override public void changed(ObservableValue property, Object oldIndex, Object newIndex) { // get the value of the new index T lValue = (T)getDataProvider().getValue(newIndex); // set the value valueObjectProperty.setValue(lValue); } }); This code results in an endless loop: Exception in Application start method Exception in thread "main" java.lang.RuntimeException: Exception in Application start method at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source) at com.sun.javafx.application.LauncherImpl.access$000(Unknown Source) at com.sun.javafx.application.LauncherImpl$1.run(Unknown Source) at java.lang.Thread.run(Thread.java:662) Caused by: java.lang.StackOverflowError at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) at javafx.beans.property.ObjectProperty.setValue(Unknown Source) at jfxtras.scene.control.SpinnerX$1.changed(SpinnerX.java:147) at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) at javafx.beans.property.ObjectProperty.setValue(Unknown Source) at jfxtras.scene.control.SpinnerX$2.changed(SpinnerX.java:167) at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) ... To solve this, I have to explicitly check if the value changed before the setValue (see "equals" below). Isn't Property suppose to do this? // react to changes of the value this.valueObjectProperty.addListener(new ChangeListener() { @Override public void changed(ObservableValue property, T oldValue, T newValue) { // get the value of the new index Object lIdx = getDataProvider().getIndex(newValue); if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); // set the value if (SpinnerX.equals(indexObjectProperty.getValue(), lIdx) == false) { indexObjectProperty.setValue(lIdx); } } }); // react to changes of the index this.indexObjectProperty.addListener(new ChangeListener() { @Override public void changed(ObservableValue property, Object oldIndex, Object newIndex) { // get the value of the new index T lValue = (T)getDataProvider().getValue(newIndex); // set the value if (SpinnerX.equals(valueObjectProperty.getValue(), lValue) == false) { valueObjectProperty.setValue(lValue); } } }); From hang.vo at oracle.com Tue Jan 17 06:52:21 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 17 Jan 2012 14:52:21 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120117145221.A583F4799B@hg.openjdk.java.net> Changeset: ce1592dd4a9b Author: hudson Date: 2012-01-11 11:00 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/ce1592dd4a9b Added tag 2.1-b08 for changeset 42d3501de782 ! .hgtags Changeset: f3e8d2e6cdf3 Author: David Grieve Date: 2012-01-17 09:44 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/f3e8d2e6cdf3 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt From richard.bair at oracle.com Tue Jan 17 08:07:20 2012 From: richard.bair at oracle.com (Richard Bair) Date: Tue, 17 Jan 2012 08:07:20 -0800 Subject: endless loop on property sync code In-Reply-To: <4F157905.1050702@tbee.org> References: <4F157905.1050702@tbee.org> Message-ID: <46C5AF66-746B-4FA9-ACAB-2EDC3F801662@oracle.com> I would have thought so. WIth an InvalidationListener we cannot make this check, because we don't actually know what the new value is unless we ask for the new value -- which would make everything eager. But for a change event we should only actually send a change event notification if it has actually changed. Sounds like a bug. Richard On Jan 17, 2012, at 5:35 AM, Tom Eugelink wrote: > > In an attempt to make some code "cheaper" I decided it could be a good idea to redundantly store the index next to the value it pointed to. Since this could save a fairly costly "getIndex" call. > > So I ended up with two properties; one public value and one private index. Basically both denote the same value, so I needed to add sychronisation code: > > // react to changes of the value > this.valueObjectProperty.addListener(new ChangeListener() > { > @Override > public void changed(ObservableValue property, T oldValue, T newValue) > { > // get the value of the new index > Object lIdx = getDataProvider().getIndex(newValue); > if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); > > // set the value > indexObjectProperty.setValue(lIdx); > } > }); > > // react to changes of the index > this.indexObjectProperty.addListener(new ChangeListener() > { > @Override > public void changed(ObservableValue property, Object oldIndex, Object newIndex) > { > // get the value of the new index > T lValue = (T)getDataProvider().getValue(newIndex); > > // set the value > valueObjectProperty.setValue(lValue); > } > }); > > > This code results in an endless loop: > Exception in Application start method > Exception in thread "main" java.lang.RuntimeException: Exception in Application start method > at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source) > at com.sun.javafx.application.LauncherImpl.access$000(Unknown Source) > at com.sun.javafx.application.LauncherImpl$1.run(Unknown Source) > at java.lang.Thread.run(Thread.java:662) > Caused by: java.lang.StackOverflowError > at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) > at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) > at javafx.beans.property.ObjectProperty.setValue(Unknown Source) > at jfxtras.scene.control.SpinnerX$1.changed(SpinnerX.java:147) > at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) > at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) > at javafx.beans.property.ObjectProperty.setValue(Unknown Source) > at jfxtras.scene.control.SpinnerX$2.changed(SpinnerX.java:167) > at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) > at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) > at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) > ... > > > To solve this, I have to explicitly check if the value changed before the setValue (see "equals" below). Isn't Property suppose to do this? > > // react to changes of the value > this.valueObjectProperty.addListener(new ChangeListener() > { > @Override > public void changed(ObservableValue property, T oldValue, T newValue) > { > // get the value of the new index > Object lIdx = getDataProvider().getIndex(newValue); > if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); > > // set the value > if (SpinnerX.equals(indexObjectProperty.getValue(), lIdx) == false) > { > indexObjectProperty.setValue(lIdx); > } > } > }); > > // react to changes of the index > this.indexObjectProperty.addListener(new ChangeListener() > { > @Override > public void changed(ObservableValue property, Object oldIndex, Object newIndex) > { > // get the value of the new index > T lValue = (T)getDataProvider().getValue(newIndex); > > // set the value > if (SpinnerX.equals(valueObjectProperty.getValue(), lValue) == false) > { > valueObjectProperty.setValue(lValue); > } > } > }); > > From hang.vo at oracle.com Tue Jan 17 09:42:14 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 17 Jan 2012 17:42:14 +0000 Subject: hg: openjfx/2.1/master/rt: [TEST ONLY] Disablle tests in TreeViewKeyInputTest since they continue to fail Message-ID: <20120117174214.E4D9C4799F@hg.openjdk.java.net> Changeset: f3f5356370df Author: David Grieve Date: 2012-01-17 12:32 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/f3f5356370df [TEST ONLY] Disablle tests in TreeViewKeyInputTest since they continue to fail ! javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java From tbee at tbee.org Tue Jan 17 09:56:23 2012 From: tbee at tbee.org (Tom Eugelink) Date: Tue, 17 Jan 2012 18:56:23 +0100 Subject: endless loop on property sync code In-Reply-To: <46C5AF66-746B-4FA9-ACAB-2EDC3F801662@oracle.com> References: <4F157905.1050702@tbee.org> <46C5AF66-746B-4FA9-ACAB-2EDC3F801662@oracle.com> Message-ID: <4F15B647.5030408@tbee.org> I'll file a bug, but Jira reports it is locked (?), so I'll try later. On 17-1-2012 17:07, Richard Bair wrote: > I would have thought so. WIth an InvalidationListener we cannot make this check, because we don't actually know what the new value is unless we ask for the new value -- which would make everything eager. But for a change event we should only actually send a change event notification if it has actually changed. Sounds like a bug. > > Richard > > On Jan 17, 2012, at 5:35 AM, Tom Eugelink wrote: > >> In an attempt to make some code "cheaper" I decided it could be a good idea to redundantly store the index next to the value it pointed to. Since this could save a fairly costly "getIndex" call. >> >> So I ended up with two properties; one public value and one private index. Basically both denote the same value, so I needed to add sychronisation code: >> >> // react to changes of the value >> this.valueObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, T oldValue, T newValue) >> { >> // get the value of the new index >> Object lIdx = getDataProvider().getIndex(newValue); >> if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); >> >> // set the value >> indexObjectProperty.setValue(lIdx); >> } >> }); >> >> // react to changes of the index >> this.indexObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, Object oldIndex, Object newIndex) >> { >> // get the value of the new index >> T lValue = (T)getDataProvider().getValue(newIndex); >> >> // set the value >> valueObjectProperty.setValue(lValue); >> } >> }); >> >> >> This code results in an endless loop: >> Exception in Application start method >> Exception in thread "main" java.lang.RuntimeException: Exception in Application start method >> at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source) >> at com.sun.javafx.application.LauncherImpl.access$000(Unknown Source) >> at com.sun.javafx.application.LauncherImpl$1.run(Unknown Source) >> at java.lang.Thread.run(Thread.java:662) >> Caused by: java.lang.StackOverflowError >> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) >> at javafx.beans.property.ObjectProperty.setValue(Unknown Source) >> at jfxtras.scene.control.SpinnerX$1.changed(SpinnerX.java:147) >> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) >> at javafx.beans.property.ObjectProperty.setValue(Unknown Source) >> at jfxtras.scene.control.SpinnerX$2.changed(SpinnerX.java:167) >> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >> ... >> >> >> To solve this, I have to explicitly check if the value changed before the setValue (see "equals" below). Isn't Property suppose to do this? >> >> // react to changes of the value >> this.valueObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, T oldValue, T newValue) >> { >> // get the value of the new index >> Object lIdx = getDataProvider().getIndex(newValue); >> if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); >> >> // set the value >> if (SpinnerX.equals(indexObjectProperty.getValue(), lIdx) == false) >> { >> indexObjectProperty.setValue(lIdx); >> } >> } >> }); >> >> // react to changes of the index >> this.indexObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, Object oldIndex, Object newIndex) >> { >> // get the value of the new index >> T lValue = (T)getDataProvider().getValue(newIndex); >> >> // set the value >> if (SpinnerX.equals(valueObjectProperty.getValue(), lValue) == false) >> { >> valueObjectProperty.setValue(lValue); >> } >> } >> }); >> >> > From richard.bair at oracle.com Tue Jan 17 10:32:09 2012 From: richard.bair at oracle.com (Richard Bair) Date: Tue, 17 Jan 2012 10:32:09 -0800 Subject: endless loop on property sync code In-Reply-To: <4F15B647.5030408@tbee.org> References: <4F157905.1050702@tbee.org> <46C5AF66-746B-4FA9-ACAB-2EDC3F801662@oracle.com> <4F15B647.5030408@tbee.org> Message-ID: <6C01B5FC-22FB-4340-B7DB-86755B4C51F4@oracle.com> Ya, sorry about that. Today being "Feature Freeze" seemed like a perfect day for JIRA to blow up :-). We've got a service request in with the Kenai guys, probably some file handle limit has been exceeded or something. Richard On Jan 17, 2012, at 9:56 AM, Tom Eugelink wrote: > > I'll file a bug, but Jira reports it is locked (?), so I'll try later. > > > On 17-1-2012 17:07, Richard Bair wrote: >> I would have thought so. WIth an InvalidationListener we cannot make this check, because we don't actually know what the new value is unless we ask for the new value -- which would make everything eager. But for a change event we should only actually send a change event notification if it has actually changed. Sounds like a bug. >> >> Richard >> >> On Jan 17, 2012, at 5:35 AM, Tom Eugelink wrote: >> >>> In an attempt to make some code "cheaper" I decided it could be a good idea to redundantly store the index next to the value it pointed to. Since this could save a fairly costly "getIndex" call. >>> >>> So I ended up with two properties; one public value and one private index. Basically both denote the same value, so I needed to add sychronisation code: >>> >>> // react to changes of the value >>> this.valueObjectProperty.addListener(new ChangeListener() >>> { >>> @Override >>> public void changed(ObservableValue property, T oldValue, T newValue) >>> { >>> // get the value of the new index >>> Object lIdx = getDataProvider().getIndex(newValue); >>> if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); >>> >>> // set the value >>> indexObjectProperty.setValue(lIdx); >>> } >>> }); >>> >>> // react to changes of the index >>> this.indexObjectProperty.addListener(new ChangeListener() >>> { >>> @Override >>> public void changed(ObservableValue property, Object oldIndex, Object newIndex) >>> { >>> // get the value of the new index >>> T lValue = (T)getDataProvider().getValue(newIndex); >>> >>> // set the value >>> valueObjectProperty.setValue(lValue); >>> } >>> }); >>> >>> >>> This code results in an endless loop: >>> Exception in Application start method >>> Exception in thread "main" java.lang.RuntimeException: Exception in Application start method >>> at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source) >>> at com.sun.javafx.application.LauncherImpl.access$000(Unknown Source) >>> at com.sun.javafx.application.LauncherImpl$1.run(Unknown Source) >>> at java.lang.Thread.run(Thread.java:662) >>> Caused by: java.lang.StackOverflowError >>> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) >>> at javafx.beans.property.ObjectProperty.setValue(Unknown Source) >>> at jfxtras.scene.control.SpinnerX$1.changed(SpinnerX.java:147) >>> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) >>> at javafx.beans.property.ObjectProperty.setValue(Unknown Source) >>> at jfxtras.scene.control.SpinnerX$2.changed(SpinnerX.java:167) >>> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >>> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >>> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >>> ... >>> >>> >>> To solve this, I have to explicitly check if the value changed before the setValue (see "equals" below). Isn't Property suppose to do this? >>> >>> // react to changes of the value >>> this.valueObjectProperty.addListener(new ChangeListener() >>> { >>> @Override >>> public void changed(ObservableValue property, T oldValue, T newValue) >>> { >>> // get the value of the new index >>> Object lIdx = getDataProvider().getIndex(newValue); >>> if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); >>> >>> // set the value >>> if (SpinnerX.equals(indexObjectProperty.getValue(), lIdx) == false) >>> { >>> indexObjectProperty.setValue(lIdx); >>> } >>> } >>> }); >>> >>> // react to changes of the index >>> this.indexObjectProperty.addListener(new ChangeListener() >>> { >>> @Override >>> public void changed(ObservableValue property, Object oldIndex, Object newIndex) >>> { >>> // get the value of the new index >>> T lValue = (T)getDataProvider().getValue(newIndex); >>> >>> // set the value >>> if (SpinnerX.equals(valueObjectProperty.getValue(), lValue) == false) >>> { >>> valueObjectProperty.setValue(lValue); >>> } >>> } >>> }); >>> >>> >> > > From hang.vo at oracle.com Tue Jan 17 10:42:09 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 17 Jan 2012 18:42:09 +0000 Subject: hg: openjfx/2.1/master/rt: [TEST ONLY] fix TreeViewKeyInputTest compile error Message-ID: <20120117184209.7B24D479A0@hg.openjdk.java.net> Changeset: 9b28816ac499 Author: David Grieve Date: 2012-01-17 13:39 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/9b28816ac499 [TEST ONLY] fix TreeViewKeyInputTest compile error ! javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java From hang.vo at oracle.com Tue Jan 17 13:33:12 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 17 Jan 2012 21:33:12 +0000 Subject: hg: openjfx/2.1/master/rt: Attempt to provide default JFXRT_HOME if the environment variable isn't set. Message-ID: <20120117213313.74BDA479A8@hg.openjdk.java.net> Changeset: 049118ba62b5 Author: jgiles Date: 2012-01-18 10:18 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/049118ba62b5 Attempt to provide default JFXRT_HOME if the environment variable isn't set. ! build-defs.xml From jasper.potts at oracle.com Tue Jan 17 13:55:32 2012 From: jasper.potts at oracle.com (Jasper Potts) Date: Tue, 17 Jan 2012 13:55:32 -0800 Subject: API addition to SplitPane In-Reply-To: <98C22A32-4DCD-440B-BE1C-D2F5FD66D712@oracle.com> References: <4F0F3CF9.1010406@oracle.com> <98C22A32-4DCD-440B-BE1C-D2F5FD66D712@oracle.com> Message-ID: <16EEE2E5-F472-49A2-96C9-2E3F4730ED17@oracle.com> I don't think we need the added complexity of weight or priority system. This will add a whole pile of complexity to the implementation and we can not find any example use cases. It also opens up dozens of new edge cases which we would need to define and many do not have a elegant answer. The simple boolean property is all that is needed to solve the very common use case of a application with a main content area and 1 or more side panels. Where the main content area is resized on window resize and the side panels will remain the same size. All applications we found fall into this use case. Jasper On Jan 13, 2012, at 10:32 AM, Richard Bair wrote: > On Jan 12, 2012, at 12:22 PM, Daniel Zwolenski wrote: > >> Just wondering if there's a case for making it a weighting system (not sure >> how useful this would be, but worth a quick thought): >> >> resizeWeight(Node node, float weight) >> >> Where the weight determines what percentage of the 'new' space the node >> gets (and how much it loses when shrinking) and a weight of zero is >> equivalent to your 'false'. > > My initial reaction was the same, although I would have used the "ALWAYS", "SOMETIMES", "NEVER" system used by GridPane. > > Jasper, can you articulate why you think the boolean is preferable? > >> Also, need to handle the case when the parent gets so small that all the >> 'resizable' nodes are already at zero. I assume in this case the nodes with >> resizable=false will actually start to shrink (alternatively the parent >> pane should not be allowed to go any smaller). > > Ya, basically this is a system of layout requests "Please, I'd prefer not to be resized" but if it has to, it will. Note that a setMinWidth etc is going onto Stage in a few days here, so at least you will be able to create a window where the user cannot shrink it past a certain point. > > Richard > From steve.x.northover at oracle.com Tue Jan 17 14:20:11 2012 From: steve.x.northover at oracle.com (steve.x.northover at oracle.com) Date: Tue, 17 Jan 2012 17:20:11 -0500 Subject: API addition to SplitPane In-Reply-To: <16EEE2E5-F472-49A2-96C9-2E3F4730ED17@oracle.com> References: <4F0F3CF9.1010406@oracle.com> <98C22A32-4DCD-440B-BE1C-D2F5FD66D712@oracle.com> <16EEE2E5-F472-49A2-96C9-2E3F4730ED17@oracle.com> Message-ID: <4F15F41B.3000706@oracle.com> Weighting systems are quite common but I agree with Jasper here. As long as we haven't closed the door to one in future should we decide to go that way. Steve On 17/01/2012 4:55 PM, Jasper Potts wrote: > I don't think we need the added complexity of weight or priority system. This will add a whole pile of complexity to the implementation and we can not find any example use cases. It also opens up dozens of new edge cases which we would need to define and many do not have a elegant answer. > > The simple boolean property is all that is needed to solve the very common use case of a application with a main content area and 1 or more side panels. Where the main content area is resized on window resize and the side panels will remain the same size. All applications we found fall into this use case. > > Jasper > > On Jan 13, 2012, at 10:32 AM, Richard Bair wrote: > >> On Jan 12, 2012, at 12:22 PM, Daniel Zwolenski wrote: >> >>> Just wondering if there's a case for making it a weighting system (not sure >>> how useful this would be, but worth a quick thought): >>> >>> resizeWeight(Node node, float weight) >>> >>> Where the weight determines what percentage of the 'new' space the node >>> gets (and how much it loses when shrinking) and a weight of zero is >>> equivalent to your 'false'. >> My initial reaction was the same, although I would have used the "ALWAYS", "SOMETIMES", "NEVER" system used by GridPane. >> >> Jasper, can you articulate why you think the boolean is preferable? >> >>> Also, need to handle the case when the parent gets so small that all the >>> 'resizable' nodes are already at zero. I assume in this case the nodes with >>> resizable=false will actually start to shrink (alternatively the parent >>> pane should not be allowed to go any smaller). >> Ya, basically this is a system of layout requests "Please, I'd prefer not to be resized" but if it has to, it will. Note that a setMinWidth etc is going onto Stage in a few days here, so at least you will be able to create a window where the user cannot shrink it past a certain point. >> >> Richard >> From hang.vo at oracle.com Tue Jan 17 15:52:15 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 17 Jan 2012 23:52:15 +0000 Subject: hg: openjfx/2.1/master/rt: 7 new changesets Message-ID: <20120117235217.84587479AB@hg.openjdk.java.net> Changeset: 297acda36ed2 Author: leifs Date: 2012-01-10 15:48 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/297acda36ed2 RT-18517: Updated GlobalMenuAdapter to not bind the ToggleGroup ! javafx-ui-controls/src/com/sun/javafx/scene/control/GlobalMenuAdapter.java Changeset: bb24d4a4d866 Author: prr Date: 2012-01-11 13:59 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/bb24d4a4d866 Fix RT-11804: New API to select LCD Text Rendering ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css ! test-stub-toolkit/src/com/sun/javafx/pgstub/StubText.java Changeset: 79e10e4a0b52 Author: kcr Date: 2012-01-12 08:23 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/79e10e4a0b52 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt Changeset: 8e49fb3d64ea Author: kcr Date: 2012-01-13 05:32 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/8e49fb3d64ea RT-17964: Wire up Glass EventLoop API to Quantum/FX Reviewed by Artem, Anthony, Henry ! test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java Changeset: 7572020a30ad Author: leifs Date: 2012-01-13 14:47 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/7572020a30ad Fixed RT-5842: New Menu API should support concept of a single appwide menubar, and work properly on Mac ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuBarSkin.java ! javafx-ui-controls/src/javafx/scene/control/MenuBar.java Changeset: a75035c2f8cc Author: Lubomir Nerad Date: 2012-01-16 12:42 +0100 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/a75035c2f8cc Fix for RT-18865: added null checks to event handler / filter registration / unregistration methods ! javafx-concurrent/src/javafx/concurrent/Service.java ! javafx-concurrent/src/javafx/concurrent/Task.java ! javafx-ui-controls/src/javafx/scene/control/MenuItem.java ! javafx-ui-controls/src/javafx/scene/control/TableColumn.java ! javafx-ui-controls/src/javafx/scene/control/TreeItem.java ! javafx-ui-controls/test/javafx/scene/control/MenuItemTest.java ! javafx-ui-controls/test/javafx/scene/control/MenuTest.java Changeset: 2fc581846b26 Author: David Grieve Date: 2012-01-17 18:02 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/2fc581846b26 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt From dalibor.topic at oracle.com Tue Jan 17 16:12:40 2012 From: dalibor.topic at oracle.com (Dalibor Topic) Date: Wed, 18 Jan 2012 01:12:40 +0100 Subject: Fwd: OpenJDK wikis have moved In-Reply-To: <4F160D49.3090107@oracle.com> References: <4F160D49.3090107@oracle.com> Message-ID: <4F160E78.6080805@oracle.com> -------- Original Message -------- Subject: OpenJDK wikis have moved Date: Wed, 18 Jan 2012 01:07:37 +0100 From: Dalibor Topic To: web-discuss at openjdk.java.net See http://robilad.livejournal.com/111187.html . cheers, dalibor topic -- Oracle Dalibor Topic | Java F/OSS Ambassador Phone: +494023646738 | Mobile: +491772664192 Oracle Java Platform Group ORACLE Deutschland B.V. & Co. KG | Nagelsweg 55 | 20097 Hamburg ORACLE Deutschland B.V. & Co. KG Hauptverwaltung: Riesstr. 25, D-80992 M?nchen Registergericht: Amtsgericht M?nchen, HRA 95603 Gesch?ftsf?hrer: J?rgen Kunz Komplement?rin: ORACLE Deutschland Verwaltung B.V. Hertogswetering 163/167, 3543 AS Utrecht, Niederlande Handelsregister der Handelskammer Midden-Niederlande, Nr. 30143697 Gesch?ftsf?hrer: Alexander van der Ven, Astrid Kepper, Val Maher Green Oracle Oracle is committed to developing practices and products that help protect the environment From hang.vo at oracle.com Tue Jan 17 18:02:48 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 18 Jan 2012 02:02:48 +0000 Subject: hg: openjfx/2.1/master/rt: fixed RT-18931 ChoiceBox NPE when StageStyle has been changed twice Message-ID: <20120118020248.CDEE5479AD@hg.openjdk.java.net> Changeset: 05002e14187b Author: psomashe Date: 2012-01-17 17:59 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/05002e14187b fixed RT-18931 ChoiceBox NPE when StageStyle has been changed twice ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ContextMenuContent.java From tbee at tbee.org Tue Jan 17 23:34:40 2012 From: tbee at tbee.org (Tom Eugelink) Date: Wed, 18 Jan 2012 08:34:40 +0100 Subject: endless loop on property sync code In-Reply-To: <46C5AF66-746B-4FA9-ACAB-2EDC3F801662@oracle.com> References: <4F157905.1050702@tbee.org> <46C5AF66-746B-4FA9-ACAB-2EDC3F801662@oracle.com> Message-ID: <4F167610.3060907@tbee.org> http://javafx-jira.kenai.com/browse/RT-18963 On 2012-01-17 17:07, Richard Bair wrote: > I would have thought so. WIth an InvalidationListener we cannot make this check, because we don't actually know what the new value is unless we ask for the new value -- which would make everything eager. But for a change event we should only actually send a change event notification if it has actually changed. Sounds like a bug. > > Richard > > On Jan 17, 2012, at 5:35 AM, Tom Eugelink wrote: > >> In an attempt to make some code "cheaper" I decided it could be a good idea to redundantly store the index next to the value it pointed to. Since this could save a fairly costly "getIndex" call. >> >> So I ended up with two properties; one public value and one private index. Basically both denote the same value, so I needed to add sychronisation code: >> >> // react to changes of the value >> this.valueObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, T oldValue, T newValue) >> { >> // get the value of the new index >> Object lIdx = getDataProvider().getIndex(newValue); >> if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); >> >> // set the value >> indexObjectProperty.setValue(lIdx); >> } >> }); >> >> // react to changes of the index >> this.indexObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, Object oldIndex, Object newIndex) >> { >> // get the value of the new index >> T lValue = (T)getDataProvider().getValue(newIndex); >> >> // set the value >> valueObjectProperty.setValue(lValue); >> } >> }); >> >> >> This code results in an endless loop: >> Exception in Application start method >> Exception in thread "main" java.lang.RuntimeException: Exception in Application start method >> at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source) >> at com.sun.javafx.application.LauncherImpl.access$000(Unknown Source) >> at com.sun.javafx.application.LauncherImpl$1.run(Unknown Source) >> at java.lang.Thread.run(Thread.java:662) >> Caused by: java.lang.StackOverflowError >> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) >> at javafx.beans.property.ObjectProperty.setValue(Unknown Source) >> at jfxtras.scene.control.SpinnerX$1.changed(SpinnerX.java:147) >> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.set(Unknown Source) >> at javafx.beans.property.ObjectProperty.setValue(Unknown Source) >> at jfxtras.scene.control.SpinnerX$2.changed(SpinnerX.java:167) >> at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(Unknown Source) >> at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(Unknown Source) >> at javafx.beans.property.ObjectPropertyBase.markInvalid(Unknown Source) >> ... >> >> >> To solve this, I have to explicitly check if the value changed before the setValue (see "equals" below). Isn't Property suppose to do this? >> >> // react to changes of the value >> this.valueObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, T oldValue, T newValue) >> { >> // get the value of the new index >> Object lIdx = getDataProvider().getIndex(newValue); >> if (lIdx == null) throw new IllegalArgumentException("Value does not exist " + newValue); >> >> // set the value >> if (SpinnerX.equals(indexObjectProperty.getValue(), lIdx) == false) >> { >> indexObjectProperty.setValue(lIdx); >> } >> } >> }); >> >> // react to changes of the index >> this.indexObjectProperty.addListener(new ChangeListener() >> { >> @Override >> public void changed(ObservableValue property, Object oldIndex, Object newIndex) >> { >> // get the value of the new index >> T lValue = (T)getDataProvider().getValue(newIndex); >> >> // set the value >> if (SpinnerX.equals(valueObjectProperty.getValue(), lValue) == false) >> { >> valueObjectProperty.setValue(lValue); >> } >> } >> }); >> >> > From tom.schindl at bestsolution.at Wed Jan 18 09:01:58 2012 From: tom.schindl at bestsolution.at (Tom Schindl) Date: Wed, 18 Jan 2012 18:01:58 +0100 Subject: Is there any resource on writing effects? Message-ID: <4F16FB06.2080109@bestsolution.at> Hi, Is there any resource or pointers to implementation effects beside the default ones provided by JavaFX 2.x? I'm currently working on an SVG to JavaFX 2.0 (well FXML) converter [1] and next on my list of things to implement is support for effects (in svg named filters [2]). Any pointers on how one might start with this are hightly appreciated. Tom [1]http://tomsondev.bestsolution.at/2012/01/16/svg-for-fxml-conversion/ [2]http://www.w3.org/TR/2011/REC-SVG11-20110816/filters.html -- B e s t S o l u t i o n . a t EDV Systemhaus GmbH ------------------------------------------------------------------------ tom schindl gesch?ftsf?hrer/CEO ------------------------------------------------------------------------ eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 http://www.BestSolution.at phone ++43 512 935834 From richard.bair at oracle.com Wed Jan 18 09:23:31 2012 From: richard.bair at oracle.com (Richard Bair) Date: Wed, 18 Jan 2012 09:23:31 -0800 Subject: Is there any resource on writing effects? In-Reply-To: <4F16FB06.2080109@bestsolution.at> References: <4F16FB06.2080109@bestsolution.at> Message-ID: <20E24F1F-2463-4C2D-8722-18F2ADF7D1F4@oracle.com> I don't think this is possible at present. On Jan 18, 2012, at 9:01 AM, Tom Schindl wrote: > Hi, > > Is there any resource or pointers to implementation effects beside the > default ones provided by JavaFX 2.x? > > I'm currently working on an SVG to JavaFX 2.0 (well FXML) converter [1] > and next on my list of things to implement is support for effects (in > svg named filters [2]). > > Any pointers on how one might start with this are hightly appreciated. > > Tom > > [1]http://tomsondev.bestsolution.at/2012/01/16/svg-for-fxml-conversion/ > [2]http://www.w3.org/TR/2011/REC-SVG11-20110816/filters.html > > -- > B e s t S o l u t i o n . a t EDV Systemhaus GmbH > ------------------------------------------------------------------------ > tom schindl gesch?ftsf?hrer/CEO > ------------------------------------------------------------------------ > eduard-bodem-gasse 5-7/1 A-6020 innsbruck fax ++43 512 935833 > http://www.BestSolution.at phone ++43 512 935834 From hang.vo at oracle.com Wed Jan 18 19:12:02 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 19 Jan 2012 03:12:02 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120119031203.9A15E479E5@hg.openjdk.java.net> Changeset: 56dfa29a01f2 Author: Kinsley Wong Date: 2012-01-18 19:00 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/56dfa29a01f2 [TEST ONLY] Unit test for SplitPane. ! javafx-ui-controls/test/javafx/scene/control/SplitPaneTest.java Changeset: dbcf74016620 Author: Kinsley Wong Date: 2012-01-18 19:06 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/dbcf74016620 RT-18855 Additional fixes for SplitPane divider positioning issues. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/SplitPaneSkin.java From hang.vo at oracle.com Thu Jan 19 14:22:08 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 19 Jan 2012 22:22:08 +0000 Subject: hg: openjfx/2.1/master/rt: RT-16589: bind LabeledSkinBase's text's fontProperty to labeled's fontProperty so that -fx-font properties for text are skipped. Reviewed by Mick, Leif, Jonathan, Richard, Jasper. Message-ID: <20120119222208.EEFFA470AC@hg.openjdk.java.net> Changeset: 6e680f18bc13 Author: David Grieve Date: 2012-01-19 17:08 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6e680f18bc13 RT-16589: bind LabeledSkinBase's text's fontProperty to labeled's fontProperty so that -fx-font properties for text are skipped. Reviewed by Mick, Leif, Jonathan, Richard, Jasper. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledSkinBase.java From hang.vo at oracle.com Thu Jan 19 14:33:27 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 19 Jan 2012 22:33:27 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120119223328.21AAD470AD@hg.openjdk.java.net> Changeset: dc3023f562bc Author: Kinsley Wong Date: 2012-01-19 14:26 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/dc3023f562bc [TEST ONLY] Better unit tests for RT-18805. ! javafx-ui-controls/test/javafx/scene/control/SplitPaneTest.java Changeset: 31e367e93df8 Author: Kinsley Wong Date: 2012-01-19 14:27 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/31e367e93df8 RT-18805: Additional fixes for SplitPane divider issues. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/SplitPaneSkin.java From hang.vo at oracle.com Thu Jan 19 15:03:01 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 19 Jan 2012 23:03:01 +0000 Subject: hg: openjfx/2.1/master/rt: RT-17903: Leak in TabPane's tab menu. Message-ID: <20120119230301.88454470AF@hg.openjdk.java.net> Changeset: 3492c5356f79 Author: Kinsley Wong Date: 2012-01-19 14:57 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3492c5356f79 RT-17903: Leak in TabPane's tab menu. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java From hang.vo at oracle.com Thu Jan 19 15:33:05 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 19 Jan 2012 23:33:05 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18928: TabPane testability issue. There is no way to get menu item in context menu of tab pane corresponding to Tab. Message-ID: <20120119233305.5F974470B1@hg.openjdk.java.net> Changeset: 16ffa41b79df Author: Kinsley Wong Date: 2012-01-19 15:23 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/16ffa41b79df RT-18928: TabPane testability issue. There is no way to get menu item in context menu of tab pane corresponding to Tab. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java From hang.vo at oracle.com Thu Jan 19 20:22:10 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 20 Jan 2012 04:22:10 +0000 Subject: hg: openjfx/2.1/master/rt: 6 new changesets Message-ID: <20120120042212.05D26470B5@hg.openjdk.java.net> Changeset: f79a432737db Author: jgiles Date: 2012-01-18 11:17 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/f79a432737db RT-18945: [ComboBox] setting new list of items brokes popup size. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java ! javafx-ui-controls/src/javafx/scene/control/ComboBox.java Changeset: da43988fb10d Author: jgiles Date: 2012-01-19 10:19 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/da43988fb10d RT-18961: TableViewKeyInputTest and TreeViewKeyInputTest fail on Mac ! javafx-ui-controls/test/javafx/scene/control/KeyEventFirer.java Changeset: b7684dc1fa3f Author: jgiles Date: 2012-01-20 10:53 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b7684dc1fa3f RT-18972: [ComboBox] value is not update, if editable mode switched off. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java ! javafx-ui-controls/src/javafx/scene/control/ComboBox.java ! javafx-ui-controls/src/javafx/scene/control/ComboBoxBase.java ! javafx-ui-controls/test/javafx/scene/control/ComboBoxTest.java Changeset: e5e4e7faf0cf Author: jgiles Date: 2012-01-20 13:55 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e5e4e7faf0cf RT-18969: ListView, selectionModel.selectedItem property doesn't change when model is changed ! javafx-ui-controls/src/javafx/scene/control/ListView.java ! javafx-ui-controls/src/javafx/scene/control/TableView.java ! javafx-ui-controls/test/javafx/scene/control/ListViewTest.java ! javafx-ui-controls/test/javafx/scene/control/TableViewTest.java Changeset: 025dacc36e76 Author: jgiles Date: 2012-01-20 17:15 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/025dacc36e76 Fixing index out of bounds exception introduced in the fix for RT-18972. ! javafx-ui-controls/src/javafx/scene/control/ListView.java Changeset: c1cc0e7893d3 Author: jgiles Date: 2012-01-20 17:16 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c1cc0e7893d3 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From hang.vo at oracle.com Fri Jan 20 10:22:08 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 20 Jan 2012 18:22:08 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-18270: creating a NumberAxis causes NPE. Message-ID: <20120120182209.10DBD470CB@hg.openjdk.java.net> Changeset: b3a9c6786253 Author: psomashe Date: 2012-01-20 10:17 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b3a9c6786253 fix RT-18270: creating a NumberAxis causes NPE. ! javafx-ui-controls/src/javafx/scene/chart/Axis.java From hang.vo at oracle.com Fri Jan 20 11:02:54 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 20 Jan 2012 19:02:54 +0000 Subject: hg: openjfx/2.1/master/rt: Fixed RT-18995: FXML-LoginDemo username textbox text does not display while typing Message-ID: <20120120190254.85FF6470CC@hg.openjdk.java.net> Changeset: e54287f1dcdc Author: leifs Date: 2012-01-20 10:51 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e54287f1dcdc Fixed RT-18995: FXML-LoginDemo username textbox text does not display while typing ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java From hang.vo at oracle.com Fri Jan 20 12:52:02 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 20 Jan 2012 20:52:02 +0000 Subject: hg: openjfx/2.1/master/rt: RT-19062: text fill was missing from LabeledImpl Message-ID: <20120120205203.18AFE470D1@hg.openjdk.java.net> Changeset: fbb1b38c01d3 Author: David Grieve Date: 2012-01-20 15:45 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/fbb1b38c01d3 RT-19062: text fill was missing from LabeledImpl ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledImpl.java From zonski at googlemail.com Fri Jan 20 12:57:11 2012 From: zonski at googlemail.com (Daniel Zwolenski) Date: Sat, 21 Jan 2012 06:57:11 +1000 Subject: Backwards compatibility and auto updates Message-ID: Hey guys, I've touched on this before but I am a bit (ok, very) worried about backwards compatibility and auto-updates, particularly in the area of visual appearance, and most obviously with CSS. Let's take a seemingly innocent fix like this one: http://javafx-jira.kenai.com/browse/RT-16589 So now suddenly when JavaFX auto-updates itself (which I have very little control over), some of my font sizes may suddenly halve if they were impacted by this fix? What if the fix was to do with visibility and suddenly an all important button is no longer visible, or to do with colours and now my white text is suddenly on a white background and can't be read. Aside from CSS, what if the fix was to an animation class, and suddenly my animation is 'fixed' so maybe a bug in the fade was making my node invisible, but now the fix makes it visible when I'm not expecting it to be, etc. This has the potential to completely break my GUI. All of these *visual* aspects can't be tested with regression testing and API compatibility tests. A UI toolkit is not like a normal Java API where we can just say, yep it compiles and the unit tests all still work, so it will be fine. Even if we could do this, would we want to? We need to be able to fix visual things, and given the incredibly loose, highly-coupled way CSS does it's selectors (one tiny change to your CSS file can change everything) it will be pretty much impossible to do this without 'breaking' existing GUIs. The problem in my mind is that we cannot 'fix' our apps to a particular version of JFX. This auto-update (and co-bundling) stuff is going to make life very hard in this field. What are your thoughts? Does anyone else share these concerns? Cheers, Dan From kevin.rushforth at oracle.com Fri Jan 20 13:08:09 2012 From: kevin.rushforth at oracle.com (kevin.rushforth at oracle.com) Date: Fri, 20 Jan 2012 21:08:09 +0000 Subject: hg: openjfx/2.1/master: Partial RT-19064: initial top-level build file from Jonathan Giles Message-ID: <20120120210809.8D01F470D2@hg.openjdk.java.net> Changeset: 5f8cbf996f33 Author: kcr Date: 2012-01-20 13:07 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rev/5f8cbf996f33 Partial RT-19064: initial top-level build file from Jonathan Giles + build.xml From richard.bair at oracle.com Fri Jan 20 13:14:29 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 20 Jan 2012 13:14:29 -0800 Subject: Backwards compatibility and auto updates In-Reply-To: References: Message-ID: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> How are you deploying your apps? On Jan 20, 2012, at 12:57 PM, Daniel Zwolenski wrote: > Hey guys, > > I've touched on this before but I am a bit (ok, very) worried about > backwards compatibility and auto-updates, particularly in the area of > visual appearance, and most obviously with CSS. > > Let's take a seemingly innocent fix like this one: > http://javafx-jira.kenai.com/browse/RT-16589 > > So now suddenly when JavaFX auto-updates itself (which I have very little > control over), some of my font sizes may suddenly halve if they were > impacted by this fix? > > What if the fix was to do with visibility and suddenly an all important > button is no longer visible, or to do with colours and now my white text is > suddenly on a white background and can't be read. Aside from CSS, what if > the fix was to an animation class, and suddenly my animation is 'fixed' so > maybe a bug in the fade was making my node invisible, but now the fix makes > it visible when I'm not expecting it to be, etc. This has the potential to > completely break my GUI. > > All of these *visual* aspects can't be tested with regression testing and > API compatibility tests. A UI toolkit is not like a normal Java API where > we can just say, yep it compiles and the unit tests all still work, so it > will be fine. Even if we could do this, would we want to? We need to be > able to fix visual things, and given the incredibly loose, highly-coupled > way CSS does it's selectors (one tiny change to your CSS file can change > everything) it will be pretty much impossible to do this without 'breaking' > existing GUIs. > > The problem in my mind is that we cannot 'fix' our apps to a particular > version of JFX. This auto-update (and co-bundling) stuff is going to make > life very hard in this field. > > What are your thoughts? Does anyone else share these concerns? > > Cheers, > Dan From hang.vo at oracle.com Fri Jan 20 13:42:07 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 20 Jan 2012 21:42:07 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120120214207.C2670470DA@hg.openjdk.java.net> Changeset: cebb6cf6d439 Author: jgiles Date: 2012-01-21 10:37 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/cebb6cf6d439 [TEST ONLY] Temporarily disabling ListView/TreeView/TableView keyboard input tests as they fail with OOME and Mac OS errors in continuous builds. Tests can easily be run locally by simply commenting out the one @Ignore line that preceeds the class declaration. ! javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java ! javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java ! javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java Changeset: 0d061c4a6c44 Author: jgiles Date: 2012-01-21 10:38 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/0d061c4a6c44 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From zonski at googlemail.com Fri Jan 20 14:13:56 2012 From: zonski at googlemail.com (Daniel Zwolenski) Date: Sat, 21 Jan 2012 08:13:56 +1000 Subject: Backwards compatibility and auto updates In-Reply-To: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> References: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> Message-ID: <547264BB-D921-4BFE-9BDA-92B7EB7D2A3B@gmail.com> I keep oscillating between every which way possible but not yet settled on a solution I'm happy with. My ideal would be jnlp+webstart (problems with multi-step installs, too many dialogs, which-jre-am-I-using, and some odd caching issues keep making me back out of this one). Is there any option that will let me control versions though? I could bundle jfx in my jar and do a manual install (sadly losing webstart's nice version updates, OS detection, and other nice stuff) but this will only work up until co-bundling kicks in, since afaik, once it's part of the jre we won't be able to exclude the 'system' version of jfx from the classpath? On 21/01/2012, at 7:14 AM, Richard Bair wrote: > How are you deploying your apps? > > On Jan 20, 2012, at 12:57 PM, Daniel Zwolenski wrote: > >> Hey guys, >> >> I've touched on this before but I am a bit (ok, very) worried about >> backwards compatibility and auto-updates, particularly in the area of >> visual appearance, and most obviously with CSS. >> >> Let's take a seemingly innocent fix like this one: >> http://javafx-jira.kenai.com/browse/RT-16589 >> >> So now suddenly when JavaFX auto-updates itself (which I have very little >> control over), some of my font sizes may suddenly halve if they were >> impacted by this fix? >> >> What if the fix was to do with visibility and suddenly an all important >> button is no longer visible, or to do with colours and now my white text is >> suddenly on a white background and can't be read. Aside from CSS, what if >> the fix was to an animation class, and suddenly my animation is 'fixed' so >> maybe a bug in the fade was making my node invisible, but now the fix makes >> it visible when I'm not expecting it to be, etc. This has the potential to >> completely break my GUI. >> >> All of these *visual* aspects can't be tested with regression testing and >> API compatibility tests. A UI toolkit is not like a normal Java API where >> we can just say, yep it compiles and the unit tests all still work, so it >> will be fine. Even if we could do this, would we want to? We need to be >> able to fix visual things, and given the incredibly loose, highly-coupled >> way CSS does it's selectors (one tiny change to your CSS file can change >> everything) it will be pretty much impossible to do this without 'breaking' >> existing GUIs. >> >> The problem in my mind is that we cannot 'fix' our apps to a particular >> version of JFX. This auto-update (and co-bundling) stuff is going to make >> life very hard in this field. >> >> What are your thoughts? Does anyone else share these concerns? >> >> Cheers, >> Dan > From richard.bair at oracle.com Fri Jan 20 14:16:46 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 20 Jan 2012 14:16:46 -0800 Subject: Backwards compatibility and auto updates In-Reply-To: <547264BB-D921-4BFE-9BDA-92B7EB7D2A3B@gmail.com> References: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> <547264BB-D921-4BFE-9BDA-92B7EB7D2A3B@gmail.com> Message-ID: My assumption was that if you cobundle FX with your app, then you have control. True, that may also require cobundling the JRE so as not to use the system JRE. But to be honest, I'd do it that way anyhow because the JRE isn't 100% backwards compatible from release to release either. What I think would be ideal is for there to be a Sparkle like framework for Java, such that you can get the patch-in place auto-upgrade and so forth for an app written in Java, cobundled with the JRE + FX. Richard On Jan 20, 2012, at 2:13 PM, Daniel Zwolenski wrote: > I keep oscillating between every which way possible but not yet settled on a solution I'm happy with. My ideal would be jnlp+webstart (problems with multi-step installs, too many dialogs, which-jre-am-I-using, and some odd caching issues keep making me back out of this one). > > Is there any option that will let me control versions though? > > I could bundle jfx in my jar and do a manual install (sadly losing webstart's nice version updates, OS detection, and other nice stuff) but this will only work up until co-bundling kicks in, since afaik, once it's part of the jre we won't be able to exclude the 'system' version of jfx from the classpath? > > > On 21/01/2012, at 7:14 AM, Richard Bair wrote: > >> How are you deploying your apps? >> >> On Jan 20, 2012, at 12:57 PM, Daniel Zwolenski wrote: >> >>> Hey guys, >>> >>> I've touched on this before but I am a bit (ok, very) worried about >>> backwards compatibility and auto-updates, particularly in the area of >>> visual appearance, and most obviously with CSS. >>> >>> Let's take a seemingly innocent fix like this one: >>> http://javafx-jira.kenai.com/browse/RT-16589 >>> >>> So now suddenly when JavaFX auto-updates itself (which I have very little >>> control over), some of my font sizes may suddenly halve if they were >>> impacted by this fix? >>> >>> What if the fix was to do with visibility and suddenly an all important >>> button is no longer visible, or to do with colours and now my white text is >>> suddenly on a white background and can't be read. Aside from CSS, what if >>> the fix was to an animation class, and suddenly my animation is 'fixed' so >>> maybe a bug in the fade was making my node invisible, but now the fix makes >>> it visible when I'm not expecting it to be, etc. This has the potential to >>> completely break my GUI. >>> >>> All of these *visual* aspects can't be tested with regression testing and >>> API compatibility tests. A UI toolkit is not like a normal Java API where >>> we can just say, yep it compiles and the unit tests all still work, so it >>> will be fine. Even if we could do this, would we want to? We need to be >>> able to fix visual things, and given the incredibly loose, highly-coupled >>> way CSS does it's selectors (one tiny change to your CSS file can change >>> everything) it will be pretty much impossible to do this without 'breaking' >>> existing GUIs. >>> >>> The problem in my mind is that we cannot 'fix' our apps to a particular >>> version of JFX. This auto-update (and co-bundling) stuff is going to make >>> life very hard in this field. >>> >>> What are your thoughts? Does anyone else share these concerns? >>> >>> Cheers, >>> Dan >> From zonski at googlemail.com Fri Jan 20 14:52:20 2012 From: zonski at googlemail.com (Daniel Zwolenski) Date: Sat, 21 Jan 2012 08:52:20 +1000 Subject: Backwards compatibility and auto updates In-Reply-To: References: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> <547264BB-D921-4BFE-9BDA-92B7EB7D2A3B@gmail.com> Message-ID: <8C7C2A61-B984-4E09-A689-266DF589FBB1@gmail.com> Co-bundling the jre in my app is overkill (and now I'd have to worry about OS versions, 32bit/64bit etc). I've never had a problem with backwards compatibility of jre's except for that one when they did that big Swing change (around 1.5, I think), which caused exactly the sort of problems I'm worried about. My GUI started looking different overnight. Swing changes had to be few and far between and the platform stagnated. There are also many great features from Webstart that are lost if I can't use it (version updates, detecting the host server, OS detection for native libraries, etc). Seems self-defeating to be losing all the good deployment features when the whole point of the tight jre integration is to make deployment easier. And, thinking bigger than my own use case, anyone wanting to deploy as an applet (the most supported deployment option), won't be able to control the jfx version. Even if they limit themselves to a specific jre, the jfx version within that jre will be the latest in the current toolset. Isn't webstart kind of most of the way there in terms of a 'sparkle framework', at least as far as post install updates are concerned? Run your app and you get the latest changes (as published by the app developer). Jnlp allows me to define which versions of which jars to use, it's just that jfx is not 'normal' - and really the only reason for its special treatment is it just happened to be developed by the same house as java, otherwise (if it were an Apache or Google initiative, say) it would have to follow all the normal rules and have to play nice. Just as a side note, slightly related to all this deployment stuff is this issue too: http://javafx-jira.kenai.com/browse/RT-18847 Deployment is the #1 hurdle I am facing in convincing people to move to jfx. The common response is "a webapp or a flash app just works, we don't want our users to have to install anything". I have no comeback - I'm currently writing a SpringMVC app (sigh) that should definitely be a jfx app but the client won't let me for the sole reason of deployment hassles. On 21/01/2012, at 8:16 AM, Richard Bair wrote: > My assumption was that if you cobundle FX with your app, then you have control. True, that may also require cobundling the JRE so as not to use the system JRE. But to be honest, I'd do it that way anyhow because the JRE isn't 100% backwards compatible from release to release either. > > What I think would be ideal is for there to be a Sparkle like framework for Java, such that you can get the patch-in place auto-upgrade and so forth for an app written in Java, cobundled with the JRE + FX. > > Richard > > On Jan 20, 2012, at 2:13 PM, Daniel Zwolenski wrote: > >> I keep oscillating between every which way possible but not yet settled on a solution I'm happy with. My ideal would be jnlp+webstart (problems with multi-step installs, too many dialogs, which-jre-am-I-using, and some odd caching issues keep making me back out of this one). >> >> Is there any option that will let me control versions though? >> >> I could bundle jfx in my jar and do a manual install (sadly losing webstart's nice version updates, OS detection, and other nice stuff) but this will only work up until co-bundling kicks in, since afaik, once it's part of the jre we won't be able to exclude the 'system' version of jfx from the classpath? >> >> >> On 21/01/2012, at 7:14 AM, Richard Bair wrote: >> >>> How are you deploying your apps? >>> >>> On Jan 20, 2012, at 12:57 PM, Daniel Zwolenski wrote: >>> >>>> Hey guys, >>>> >>>> I've touched on this before but I am a bit (ok, very) worried about >>>> backwards compatibility and auto-updates, particularly in the area of >>>> visual appearance, and most obviously with CSS. >>>> >>>> Let's take a seemingly innocent fix like this one: >>>> http://javafx-jira.kenai.com/browse/RT-16589 >>>> >>>> So now suddenly when JavaFX auto-updates itself (which I have very little >>>> control over), some of my font sizes may suddenly halve if they were >>>> impacted by this fix? >>>> >>>> What if the fix was to do with visibility and suddenly an all important >>>> button is no longer visible, or to do with colours and now my white text is >>>> suddenly on a white background and can't be read. Aside from CSS, what if >>>> the fix was to an animation class, and suddenly my animation is 'fixed' so >>>> maybe a bug in the fade was making my node invisible, but now the fix makes >>>> it visible when I'm not expecting it to be, etc. This has the potential to >>>> completely break my GUI. >>>> >>>> All of these *visual* aspects can't be tested with regression testing and >>>> API compatibility tests. A UI toolkit is not like a normal Java API where >>>> we can just say, yep it compiles and the unit tests all still work, so it >>>> will be fine. Even if we could do this, would we want to? We need to be >>>> able to fix visual things, and given the incredibly loose, highly-coupled >>>> way CSS does it's selectors (one tiny change to your CSS file can change >>>> everything) it will be pretty much impossible to do this without 'breaking' >>>> existing GUIs. >>>> >>>> The problem in my mind is that we cannot 'fix' our apps to a particular >>>> version of JFX. This auto-update (and co-bundling) stuff is going to make >>>> life very hard in this field. >>>> >>>> What are your thoughts? Does anyone else share these concerns? >>>> >>>> Cheers, >>>> Dan >>> > From hang.vo at oracle.com Fri Jan 20 15:32:40 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 20 Jan 2012 23:32:40 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-18356 MenuBar : incorrect selection. Message-ID: <20120120233240.D1570470E2@hg.openjdk.java.net> Changeset: c06a484e652e Author: Paru Somashekar Date: 2012-01-20 15:32 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c06a484e652e fix RT-18356 MenuBar : incorrect selection. RT-18960 MenuBar default foreground, background - bad contrast. - includes Jasper's caspian patch. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuBarSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css From richard.bair at oracle.com Fri Jan 20 15:33:01 2012 From: richard.bair at oracle.com (Richard Bair) Date: Fri, 20 Jan 2012 15:33:01 -0800 Subject: Backwards compatibility and auto updates In-Reply-To: <8C7C2A61-B984-4E09-A689-266DF589FBB1@gmail.com> References: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> <547264BB-D921-4BFE-9BDA-92B7EB7D2A3B@gmail.com> <8C7C2A61-B984-4E09-A689-266DF589FBB1@gmail.com> Message-ID: <3F79EEA0-C013-4D71-9382-B4F18E402A07@oracle.com> > Co-bundling the jre in my app is overkill (and now I'd have to worry about OS versions, 32bit/64bit etc). Apple's universal binary is genius :-). But really you can just do 32-bit if that's what you want. Or offer both, or co-bundle both and switch on the fly. > I've never had a problem with backwards compatibility of jre's except for that one when they did that big Swing change (around 1.5, I think), which caused exactly the sort of problems I'm worried about. My GUI started looking different overnight. Swing changes had to be few and far between and the platform stagnated. You've been lucky :-) > There are also many great features from Webstart that are lost if I can't use it (version updates, detecting the host server, OS detection for native libraries, etc). Seems self-defeating to be losing all the good deployment features when the whole point of the tight jre integration is to make deployment easier. > > And, thinking bigger than my own use case, anyone wanting to deploy as an applet (the most supported deployment option), won't be able to control the jfx version. Even if they limit themselves to a specific jre, the jfx version within that jre will be the latest in the current toolset. Actually no, the applet case is special. There are security vulnerabilities in the applet that get fixed regularly as they are discovered. This is unique to the applet in that a security vulnerability in a co-bundled JRE + App doesn't matter, since the app has full privileges anyway and therefore opens no new holes. The security manager is only used in the applet / JNLP case, so it is in these cases where we have to get away from allowing developers to target a specific version of the JRE. These bug fixes are not all backported to all old versions of the JRE, there is instead a "security baseline", or version of the JRE that forms the baseline -- you can't target versions of Java older than the baseline. Except in some number of edge cases. The whole thing is problematic and I'm afraid that the only real straightforward answer that I can see is to co-bundle. The bottom line is that in the applet case, we *have* to push people to the latest and greatest or we're going to be a constant security hole. Now, that of course makes compatibility even that much more problematic. For CSS our plan is to introduce new stylesheets in the future when the visuals need to be updated (ie: the successor to caspian) so that the old apps don't just look completely different overnight. But bug fixes will do this sort of thing to you from time to time. Really I don't see how this can work if you don't co-bundle with your applications. Any other option will lead to incompatibilities of one form or another. > Isn't webstart kind of most of the way there in terms of a 'sparkle framework', at least as far as post install updates are concerned? Run your app and you get the latest changes (as published by the app developer). Jnlp allows me to define which versions of which jars to use, it's just that jfx is not 'normal' - and really the only reason for its special treatment is it just happened to be developed by the same house as java, otherwise (if it were an Apache or Google initiative, say) it would have to follow all the normal rules and have to play nice. > > Just as a side note, slightly related to all this deployment stuff is this issue too: > http://javafx-jira.kenai.com/browse/RT-18847 > > Deployment is the #1 hurdle I am facing in convincing people to move to jfx. The common response is "a webapp or a flash app just works, we don't want our users to have to install anything". I have no comeback - I'm currently writing a SpringMVC app (sigh) that should definitely be a jfx app but the client won't let me for the sole reason of deployment hassles. I completely understand. Richard From hang.vo at oracle.com Fri Jan 20 16:12:11 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Sat, 21 Jan 2012 00:12:11 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-18859 : Return key in a TextField makes the first menu item within the menubar open. Message-ID: <20120121001211.CC363470E3@hg.openjdk.java.net> Changeset: 55346b3feef9 Author: Paru Somashekar Date: 2012-01-20 16:07 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/55346b3feef9 fix RT-18859 : Return key in a TextField makes the first menu item within the menubar open. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuBarSkin.java From hang.vo at oracle.com Fri Jan 20 17:12:03 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Sat, 21 Jan 2012 01:12:03 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120121011203.F265B470E4@hg.openjdk.java.net> Changeset: ed8104fd3efa Author: Kinsley Wong Date: 2012-01-20 16:59 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/ed8104fd3efa RT-19056: SplitPane with CSS makes the JavaFx thread enter in an infinite loop. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/SplitPaneSkin.java Changeset: 1026cc96ab13 Author: Kinsley Wong Date: 2012-01-20 17:05 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/1026cc96ab13 [API APPROVED] RT-18806: SplitPane divider should support an absolute position ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/SplitPaneSkin.java ! javafx-ui-controls/src/javafx/scene/control/SplitPane.java ! javafx-ui-controls/test/javafx/scene/control/SplitPaneTest.java From kevin.rushforth at oracle.com Sat Jan 21 11:45:45 2012 From: kevin.rushforth at oracle.com (Kevin Rushforth) Date: Sat, 21 Jan 2012 11:45:45 -0800 Subject: 2.1/master/rt changesets and build breakages Message-ID: <4F1B15E9.9040106@oracle.com> We are busy getting ready to open source more of the runtime starting in early February. Once this happens, the current approach of pushing changesets directly to the openjfx repository from the controls forest will cause the builds to break with increasing regularity. We're effectively treating the current 2.1/master repository as if it were the controls team forest, which will no longer work. As a result, we are soon going to do what we probably should have done all along: the 2.1/master forest will be just that, the master forest and only get updated once per week when we push out a new weekly build. This will keep things stable and in sync as we fix bugs and wind down the 2.1 release. For 2.2, we will set things up so we actually have a separate controls and graphics team forest on openjfx, along with master: openjfx/2.2/controls/ rt tests openjfx/2.2/graphics/ rt tests openjfx/2.2/master/ rt tests We expect to create 2.2 repositories around the beginning of March. Controls and graphics will be for those folks who want to live on the bleeding edge, while master will be the stable source base. As even more source code becomes open source in 2.2 there will be less and less reliance on the binary jfxrt.jar (until such time as that dependency disappears altogether). -- Kevin From christian.schudt at gmx.de Sun Jan 22 05:54:07 2012 From: christian.schudt at gmx.de (Christian Schudt) Date: Sun, 22 Jan 2012 14:54:07 +0100 Subject: Calendar / DatePicker control Message-ID: Hi together, I developed a Calendar / DatePicker control recently and thought you might be interested in it. As Jonathan suggested here (https://forums.oracle.com/forums/thread.jspa?threadID=2280830) I should write a mail here. I published a short description and the sources here: http://myjavafx.blogspot.com/2012/01/javafx-calendar-control.html I also read the UI specs and tried to stick to them, but some things are still missing. Kind regards, Christian From tbee at tbee.org Sun Jan 22 06:43:55 2012 From: tbee at tbee.org (Tom Eugelink) Date: Sun, 22 Jan 2012 15:43:55 +0100 Subject: Calendar / DatePicker control In-Reply-To: References: Message-ID: <4F1C20AB.5050103@tbee.org> Hi Christian, Wilkommen. We're three (calendar picker implementations) now; Sai Pradeep Dandem (no idea what the first name is ;-) did one and I've got one. Sai (yes, guessing) is currently adding his implementation to jfxtras (http://code.google.com/p/jfxtras), which kinda functions as the breeding ground for stuff for JavaFX at the moment. So maybe it is a good idea to add yours as well; CalendarPickerX3? And then we can see how we are going to get it into one big happy implementation. Tom On 2012-01-22 14:54, Christian Schudt wrote: > Hi together, > > I developed a Calendar / DatePicker control recently and thought you might be interested in it. > > As Jonathan suggested here (https://forums.oracle.com/forums/thread.jspa?threadID=2280830) I should write a mail here. > > I published a short description and the sources here: > > http://myjavafx.blogspot.com/2012/01/javafx-calendar-control.html > > I also read the UI specs and tried to stick to them, but some things are still missing. > > Kind regards, > Christian From jonathan.giles at oracle.com Sun Jan 22 12:44:03 2012 From: jonathan.giles at oracle.com (Jonathan Giles) Date: Mon, 23 Jan 2012 09:44:03 +1300 Subject: Calendar / DatePicker control In-Reply-To: References: Message-ID: <4F1C7513.7020008@oracle.com> Hi Christian, It looks like you've put a heap of effort into this calendar picker - it looks very nice (and I especially like your use of CSS and animation). As I said in the post you refer to, there are at least four calendar pickers I know of, and your one takes that to five! :-) As you know, a calendar / date picker control is missing from JavaFX, and it is something that I would love to see included as soon as possible. This control could also be an excellent community contribution via OpenJFX. I have been suggesting to all date picker controls developers to move their code to JFXtras, and to work with the other developers on creating a single control that features the best features of all of them. Of course, I know this isn't as fun as just writing and owning your own control, so feel free to do that if you want. My suggestion is to those that are keen to see their date picker possibly appear in OpenJFX. If you were to move in to JFXtras and merge your work with the other developers, I would recommend basing the foundation on Toms CalendarPickerX [1], as it is built using the full Control API that a JavaFX control should use if it were to be made public. [1] http://code.google.com/p/jfxtras/source/browse/controls/src/main/java/jfxtras/scene/control/CalendarPickerX.java -- Jonathan On 23/01/2012 2:54 a.m., Christian Schudt wrote: > Hi together, > > I developed a Calendar / DatePicker control recently and thought you might be interested in it. > > As Jonathan suggested here (https://forums.oracle.com/forums/thread.jspa?threadID=2280830) I should write a mail here. > > I published a short description and the sources here: > > http://myjavafx.blogspot.com/2012/01/javafx-calendar-control.html > > I also read the UI specs and tried to stick to them, but some things are still missing. > > Kind regards, > Christian From hang.vo at oracle.com Sun Jan 22 16:52:48 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 23 Jan 2012 00:52:48 +0000 Subject: hg: openjfx/2.1/master/rt: RT-19079: MenuBar.useSystemMenuBar needs to be dynamically switchable on/off Message-ID: <20120123005248.C7D8F4711C@hg.openjdk.java.net> Changeset: ef14df9bc8ed Author: leifs Date: 2012-01-22 16:45 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/ef14df9bc8ed RT-19079: MenuBar.useSystemMenuBar needs to be dynamically switchable on/off ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuBarSkin.java From hang.vo at oracle.com Mon Jan 23 11:03:43 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 23 Jan 2012 19:03:43 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18809: NPE in GridPaneDesignInfo.getCellBounds(). Message-ID: <20120123190343.942094713B@hg.openjdk.java.net> Changeset: d09295052fb9 Author: Kinsley Wong Date: 2012-01-23 10:56 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/d09295052fb9 RT-18809: NPE in GridPaneDesignInfo.getCellBounds(). ! javafx-designtime/src/javafx/scene/layout/GridPaneDesignInfo.java From hang.vo at oracle.com Mon Jan 23 11:42:45 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 23 Jan 2012 19:42:45 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120123194246.1D9234713C@hg.openjdk.java.net> Changeset: fc6c5cf966c4 Author: leifs Date: 2012-01-23 11:34 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/fc6c5cf966c4 Fixzed RT-19102: TextField vertical alignment is wrong ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java Changeset: b12ded4d8ff8 Author: Kinsley Wong Date: 2012-01-23 11:41 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b12ded4d8ff8 RT-18806: Rename resizableWithParent to setResizableWithParent. ! javafx-ui-controls/src/javafx/scene/control/SplitPane.java ! javafx-ui-controls/test/javafx/scene/control/SplitPaneTest.java From hang.vo at oracle.com Mon Jan 23 14:12:47 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Mon, 23 Jan 2012 22:12:47 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120123221247.B01D947140@hg.openjdk.java.net> Changeset: 6f2621ad8c61 Author: leifs Date: 2012-01-23 14:01 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6f2621ad8c61 [DOC ONLY] Fixed RT-18848: [PasswordField] descriptions of cut and copy methods are wrong. ! javafx-ui-controls/src/javafx/scene/control/PasswordField.java Changeset: ab060b88575c Author: leifs Date: 2012-01-23 14:11 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/ab060b88575c [DOC ONLY] Fixed RT-18857: [TextField] onAction is not applied. Clarified usage of onAction() handler. ! javafx-ui-controls/src/javafx/scene/control/TextField.java From dalibor.topic at oracle.com Mon Jan 23 17:27:42 2012 From: dalibor.topic at oracle.com (Dalibor Topic) Date: Mon, 23 Jan 2012 17:27:42 -0800 Subject: Feature Freeze is Imminent! In-Reply-To: References: Message-ID: <4F1E090E.8000608@oracle.com> On 1/13/12 11:00 AM, Richard Bair wrote: > Well, drat. None of the pictures made it, and the darn WIKI won't let me post a new page, keeps saying me session has timed out. I guess you'll have to go load the dashboard in JIRA and figure out which missing images go with each missing portlet :-) Are you still having that issue? cheers, dalibor topic -- Oracle Dalibor Topic | Java F/OSS Ambassador Phone: +494023646738 | Mobile: +491772664192 Oracle Java Platform Group ORACLE Deutschland B.V. & Co. KG | Nagelsweg 55 | 20097 Hamburg ORACLE Deutschland B.V. & Co. KG Hauptverwaltung: Riesstr. 25, D-80992 M?nchen Registergericht: Amtsgericht M?nchen, HRA 95603 Gesch?ftsf?hrer: J?rgen Kunz Komplement?rin: ORACLE Deutschland Verwaltung B.V. Hertogswetering 163/167, 3543 AS Utrecht, Niederlande Handelsregister der Handelskammer Midden-Niederlande, Nr. 30143697 Gesch?ftsf?hrer: Alexander van der Ven, Astrid Kepper, Val Maher Green Oracle Oracle is committed to developing practices and products that help protect the environment From hang.vo at oracle.com Mon Jan 23 19:04:16 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 24 Jan 2012 03:04:16 +0000 Subject: hg: openjfx/2.1/master/rt: 3 new changesets Message-ID: <20120124030417.EAA4F4714A@hg.openjdk.java.net> Changeset: cab753430245 Author: jgiles Date: 2012-01-24 14:38 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/cab753430245 RT-19081: TableView shows resize cursor for columns that are not resizeable ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/NestedTableColumnHeader.java Changeset: 8c5bff85d002 Author: jgiles Date: 2012-01-24 15:46 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/8c5bff85d002 RT-19077: [ComboBox] showing property API issue. ! javafx-ui-controls/src/javafx/scene/control/ComboBoxBase.java Changeset: a5e9c6a82052 Author: jgiles Date: 2012-01-24 15:49 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/a5e9c6a82052 RT-19110: ComboBox button height is incorrect when using default ListCell ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/CellSkinBase.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxBaseSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ListCellSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css From hang.vo at oracle.com Tue Jan 24 08:03:39 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 24 Jan 2012 16:03:39 +0000 Subject: hg: openjfx/2.1/master/rt: 3 new changesets Message-ID: <20120124160341.1DE8547166@hg.openjdk.java.net> Changeset: e5bcfe6d1fd6 Author: mickf Date: 2012-01-24 15:59 +0000 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e5bcfe6d1fd6 RT-18958 - ScrollPane: NullPointerException when no content is set ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollPaneSkin.java ! javafx-ui-controls/test/com/sun/javafx/scene/control/skin/ScrollPaneSkinTest.java Changeset: 6cf613e420c8 Author: David Grieve Date: 2012-01-24 10:49 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6cf613e420c8 RT-17212: replace -fx-graphic-vpos with -fx-content-display. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css Changeset: 4dd37d3f9ca9 Author: David Grieve Date: 2012-01-24 11:01 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/4dd37d3f9ca9 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From hang.vo at oracle.com Tue Jan 24 08:12:33 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 24 Jan 2012 16:12:33 +0000 Subject: hg: openjfx/2.1/master/rt: 4 new changesets Message-ID: <20120124161234.A151747167@hg.openjdk.java.net> Changeset: 627b0b1f20b6 Author: hudson Date: 2012-01-18 10:46 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/627b0b1f20b6 Added tag 2.1-b09 for changeset 2fc581846b26 ! .hgtags Changeset: c6b688029bed Author: David Grieve Date: 2012-01-24 10:46 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c6b688029bed Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt Changeset: 3c8653f1768b Author: David Grieve Date: 2012-01-24 11:02 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3c8653f1768b branch merge Changeset: c64192359e53 Author: David Grieve Date: 2012-01-24 11:03 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c64192359e53 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/rt From hang.vo at oracle.com Tue Jan 24 11:33:28 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 24 Jan 2012 19:33:28 +0000 Subject: hg: openjfx/2.1/master/rt: MenuBar wasn't refreshing when switching to system menu bar. Message-ID: <20120124193328.B125147171@hg.openjdk.java.net> Changeset: 1a40380a6c30 Author: leifs Date: 2012-01-24 11:22 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/1a40380a6c30 MenuBar wasn't refreshing when switching to system menu bar. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuBarSkin.java From hang.vo at oracle.com Tue Jan 24 14:22:43 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 24 Jan 2012 22:22:43 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18813: Accordion doesn't change pane correctly programmatically. Message-ID: <20120124222244.4516F47176@hg.openjdk.java.net> Changeset: a1590965f8b4 Author: Kinsley Wong Date: 2012-01-24 14:11 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/a1590965f8b4 RT-18813: Accordion doesn't change pane correctly programmatically. ! javafx-ui-controls/src/javafx/scene/control/Accordion.java From richard.bair at oracle.com Tue Jan 24 16:23:14 2012 From: richard.bair at oracle.com (Richard Bair) Date: Tue, 24 Jan 2012 16:23:14 -0800 Subject: Feature Freeze is Imminent! In-Reply-To: <4F1E090E.8000608@oracle.com> References: <4F1E090E.8000608@oracle.com> Message-ID: I will try again and let you know. Richard On Jan 23, 2012, at 5:27 PM, Dalibor Topic wrote: > On 1/13/12 11:00 AM, Richard Bair wrote: >> Well, drat. None of the pictures made it, and the darn WIKI won't let me post a new page, keeps saying me session has timed out. I guess you'll have to go load the dashboard in JIRA and figure out which missing images go with each missing portlet :-) > > Are you still having that issue? > > cheers, > dalibor topic > > -- > Oracle > Dalibor Topic | Java F/OSS Ambassador > Phone: +494023646738 | Mobile: +491772664192 > Oracle Java Platform Group > > ORACLE Deutschland B.V. & Co. KG | Nagelsweg 55 | 20097 Hamburg > > ORACLE Deutschland B.V. & Co. KG > Hauptverwaltung: Riesstr. 25, D-80992 M?nchen > Registergericht: Amtsgericht M?nchen, HRA 95603 > Gesch?ftsf?hrer: J?rgen Kunz > > Komplement?rin: ORACLE Deutschland Verwaltung B.V. > Hertogswetering 163/167, 3543 AS Utrecht, Niederlande > Handelsregister der Handelskammer Midden-Niederlande, Nr. 30143697 > Gesch?ftsf?hrer: Alexander van der Ven, Astrid Kepper, Val Maher > > Green Oracle Oracle is committed to developing practices and products that help protect the environment From hang.vo at oracle.com Tue Jan 24 17:22:33 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 25 Jan 2012 01:22:33 +0000 Subject: hg: openjfx/2.1/master/rt: RT-19025: Accordion computed height is incorrect. Message-ID: <20120125012233.AE8534717F@hg.openjdk.java.net> Changeset: 7603128a3d29 Author: Kinsley Wong Date: 2012-01-24 17:17 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/7603128a3d29 RT-19025: Accordion computed height is incorrect. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/AccordionSkin.java ! javafx-ui-controls/test/javafx/scene/control/AccordionTest.java From hang.vo at oracle.com Wed Jan 25 07:33:17 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 25 Jan 2012 15:33:17 +0000 Subject: hg: openjfx/2.1/master/rt: RT-17753 - [ProgressBar] confused, when height is changed Message-ID: <20120125153318.212D447192@hg.openjdk.java.net> Changeset: 9e514329f414 Author: mickf Date: 2012-01-25 15:22 +0000 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/9e514329f414 RT-17753 - [ProgressBar] confused, when height is changed ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ProgressBarSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css From hang.vo at oracle.com Wed Jan 25 09:52:36 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 25 Jan 2012 17:52:36 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-16747 NumberAxis: negative minor tick length/count Message-ID: <20120125175237.4EF4947199@hg.openjdk.java.net> Changeset: 14e09c627aa8 Author: Paru Somashekar Date: 2012-01-25 09:47 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/14e09c627aa8 fix RT-16747 NumberAxis: negative minor tick length/count ! javafx-ui-controls/src/javafx/scene/chart/ValueAxis.java From hang.vo at oracle.com Wed Jan 25 10:12:28 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 25 Jan 2012 18:12:28 +0000 Subject: hg: openjfx/2.1/master/rt: Fixed RT-19075: [ComboBox] onAction event is handled 2 times, when value is set by unidirectional binding. Message-ID: <20120125181229.164D54719A@hg.openjdk.java.net> Changeset: abf6f055572b Author: leifs Date: 2012-01-25 10:08 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/abf6f055572b Fixed RT-19075: [ComboBox] onAction event is handled 2 times, when value is set by unidirectional binding. ! javafx-ui-controls/src/javafx/scene/control/TextField.java From christian.schudt at gmx.de Wed Jan 25 10:48:45 2012 From: christian.schudt at gmx.de (Christian Schudt) Date: Wed, 25 Jan 2012 19:48:45 +0100 Subject: Calendar / DatePicker control In-Reply-To: <4F1C7513.7020008@oracle.com> References: <4F1C7513.7020008@oracle.com> Message-ID: Hi, ok, can someone/Jonathan/Tom explain me what to do exactly? I've looked at the google code and it seems I have to create a clone, where I can work on. I assume this is just like a branch. It seems there are already about 20 clones... Who will merge all this stuff? Or what is the process of getting a common code base? Honestly I doubt we could have THE one control eventually, since the code bases of the three existing calendar controls are too different. And what is the difference between JFXtras and OpenJFX? Will stuff from JFXtras be taken into OpenJFX, which eventually will be JavaFX 2.x ? Christian Am 22.01.2012 um 21:44 schrieb Jonathan Giles: > Hi Christian, > > It looks like you've put a heap of effort into this calendar picker - it looks very nice (and I especially like your use of CSS and animation). > > As I said in the post you refer to, there are at least four calendar pickers I know of, and your one takes that to five! :-) > > As you know, a calendar / date picker control is missing from JavaFX, and it is something that I would love to see included as soon as possible. This control could also be an excellent community contribution via OpenJFX. I have been suggesting to all date picker controls developers to move their code to JFXtras, and to work with the other developers on creating a single control that features the best features of all of them. > > Of course, I know this isn't as fun as just writing and owning your own control, so feel free to do that if you want. My suggestion is to those that are keen to see their date picker possibly appear in OpenJFX. > > If you were to move in to JFXtras and merge your work with the other developers, I would recommend basing the foundation on Toms CalendarPickerX [1], as it is built using the full Control API that a JavaFX control should use if it were to be made public. > > [1] http://code.google.com/p/jfxtras/source/browse/controls/src/main/java/jfxtras/scene/control/CalendarPickerX.java > > -- Jonathan > > > On 23/01/2012 2:54 a.m., Christian Schudt wrote: >> Hi together, >> >> I developed a Calendar / DatePicker control recently and thought you might be interested in it. >> >> As Jonathan suggested here (https://forums.oracle.com/forums/thread.jspa?threadID=2280830) I should write a mail here. >> >> I published a short description and the sources here: >> >> http://myjavafx.blogspot.com/2012/01/javafx-calendar-control.html >> >> I also read the UI specs and tried to stick to them, but some things are still missing. >> >> Kind regards, >> Christian From tbee at tbee.org Wed Jan 25 11:54:33 2012 From: tbee at tbee.org (Tom Eugelink) Date: Wed, 25 Jan 2012 20:54:33 +0100 Subject: Calendar / DatePicker control In-Reply-To: References: <4F1C7513.7020008@oracle.com> Message-ID: <4F205DF9.60506@tbee.org> You (probably) have cloned the repository. Any work you do, you commit against your local repository and then you sync (merge) that repository with the central one on google. This is the basics. AFAIK there are no branches. JFXtras for now is the semi official incubator for OpenJFX. We get to play and the Oracle guys (in our case Jonathan) will determine if it gets included in OpenJFX. In the end there will be one CalendarPicker that is included, and for that it must be structured as a JFX control, with a control class, behavior and skin. If you have issues with the current base Jonathan would like the control to start on, CalendarPickerX, than please open up a discussion. Probably in the JFXtras mailing list (although I can imagine keeping it here could be wise as well). You naturally are always welcome to create your own implementation or skin, as part of JFXtras or as your own project. Tom On 2012-01-25 19:48, Christian Schudt wrote: > Hi, > > ok, can someone/Jonathan/Tom explain me what to do exactly? > > I've looked at the google code and it seems I have to create a clone, where I can work on. I assume this is just like a branch. > > It seems there are already about 20 clones... Who will merge all this stuff? Or what is the process of getting a common code base? Honestly I doubt we could have THE one control eventually, since the code bases of the three existing calendar controls are too different. > > And what is the difference between JFXtras and OpenJFX? Will stuff from JFXtras be taken into OpenJFX, which eventually will be JavaFX 2.x ? > > > Christian > > > > > Am 22.01.2012 um 21:44 schrieb Jonathan Giles: > >> Hi Christian, >> >> It looks like you've put a heap of effort into this calendar picker - it looks very nice (and I especially like your use of CSS and animation). >> >> As I said in the post you refer to, there are at least four calendar pickers I know of, and your one takes that to five! :-) >> >> As you know, a calendar / date picker control is missing from JavaFX, and it is something that I would love to see included as soon as possible. This control could also be an excellent community contribution via OpenJFX. I have been suggesting to all date picker controls developers to move their code to JFXtras, and to work with the other developers on creating a single control that features the best features of all of them. >> >> Of course, I know this isn't as fun as just writing and owning your own control, so feel free to do that if you want. My suggestion is to those that are keen to see their date picker possibly appear in OpenJFX. >> >> If you were to move in to JFXtras and merge your work with the other developers, I would recommend basing the foundation on Toms CalendarPickerX [1], as it is built using the full Control API that a JavaFX control should use if it were to be made public. >> >> [1] http://code.google.com/p/jfxtras/source/browse/controls/src/main/java/jfxtras/scene/control/CalendarPickerX.java >> >> -- Jonathan >> >> >> On 23/01/2012 2:54 a.m., Christian Schudt wrote: >>> Hi together, >>> >>> I developed a Calendar / DatePicker control recently and thought you might be interested in it. >>> >>> As Jonathan suggested here (https://forums.oracle.com/forums/thread.jspa?threadID=2280830) I should write a mail here. >>> >>> I published a short description and the sources here: >>> >>> http://myjavafx.blogspot.com/2012/01/javafx-calendar-control.html >>> >>> I also read the UI specs and tried to stick to them, but some things are still missing. >>> >>> Kind regards, >>> Christian > From hang.vo at oracle.com Thu Jan 26 14:33:37 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 26 Jan 2012 22:33:37 +0000 Subject: hg: openjfx/2.1/master/rt: 3 new changesets Message-ID: <20120126223338.9FA51471DE@hg.openjdk.java.net> Changeset: 40d15360d669 Author: Kinsley Wong Date: 2012-01-25 10:12 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/40d15360d669 RT-18678: Clean up the fix for Accordion TitledPane height is not updated when Accordion is resized. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/AccordionSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TitledPaneSkin.java Changeset: e7209f24fc37 Author: Kinsley Wong Date: 2012-01-26 14:27 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e7209f24fc37 RT-18878: GridPaneDesignInfo.getCellBounds() sometimes return out of date info. ! javafx-designtime/src/javafx/scene/layout/GridPaneDesignInfo.java Changeset: 44f70f5a3bc4 Author: Kinsley Wong Date: 2012-01-26 14:28 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/44f70f5a3bc4 Merge From hang.vo at oracle.com Thu Jan 26 14:42:32 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 26 Jan 2012 22:42:32 +0000 Subject: hg: openjfx/2.1/master/rt: Fixed RT-18703: bidirecional binding not work with textfield. Message-ID: <20120126224233.17014471DF@hg.openjdk.java.net> Changeset: 756e375f5788 Author: leifs Date: 2012-01-26 14:38 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/756e375f5788 Fixed RT-18703: bidirecional binding not work with textfield. ! javafx-ui-controls/src/javafx/scene/control/TextInputControl.java From hang.vo at oracle.com Thu Jan 26 14:52:37 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 26 Jan 2012 22:52:37 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120126225238.23E0A471E4@hg.openjdk.java.net> Changeset: 10c08ea824c8 Author: Paru Somashekar Date: 2012-01-26 14:48 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/10c08ea824c8 fix broken test in ValueAxisTest ! javafx-ui-controls/src/javafx/scene/chart/ValueAxis.java Changeset: c27d38818264 Author: leifs Date: 2012-01-26 14:47 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c27d38818264 Fixed RT-18989: TextField with promptText and initial text value does not show promptText when initial text is deleted ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java From hang.vo at oracle.com Thu Jan 26 15:03:40 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 26 Jan 2012 23:03:40 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-14798 : Mac: tooltips disappear right after they are displayed Message-ID: <20120126230341.1B304471E5@hg.openjdk.java.net> Changeset: 92c2b4990fb3 Author: Paru Somashekar Date: 2012-01-26 15:03 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/92c2b4990fb3 fix RT-14798 : Mac: tooltips disappear right after they are displayed RT-17171: Somtimes Tooltip disappears almost immediately. ! javafx-ui-controls/src/javafx/scene/control/Tooltip.java From hang.vo at oracle.com Thu Jan 26 15:52:34 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Thu, 26 Jan 2012 23:52:34 +0000 Subject: hg: openjfx/2.1/master/rt: 8 new changesets Message-ID: <20120126235237.2F70E471E6@hg.openjdk.java.net> Changeset: 7dc89400e5ac Author: jgiles Date: 2012-01-25 11:12 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/7dc89400e5ac RT-18983: [ListView] Shift+home/end leads to exception ! javafx-ui-controls/src/javafx/scene/control/MultipleSelectionModelBase.java Changeset: e5cbcdd43058 Author: jgiles Date: 2012-01-25 15:55 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e5cbcdd43058 RT-18941: [ComboBox] selected index increases, if you add element after currently selected ! javafx-ui-controls/src/javafx/scene/control/ComboBox.java ! javafx-ui-controls/test/javafx/scene/control/ComboBoxTest.java Changeset: b47024ea47d3 Author: jgiles Date: 2012-01-26 10:37 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b47024ea47d3 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt Changeset: 5a90749261d3 Author: jgiles Date: 2012-01-26 16:54 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/5a90749261d3 RT-19217: fx2.1-controls-scrum-b285: RT-19081 fix lead to 40% footprint regression in TableColumn ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/NestedTableColumnHeader.java Changeset: cff7b20bd0d9 Author: jgiles Date: 2012-01-27 11:24 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/cff7b20bd0d9 RT-19227: [ComboBox] the selected index behaves strange ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java ! javafx-ui-controls/src/javafx/scene/control/ComboBox.java ! javafx-ui-controls/test/javafx/scene/control/ComboBoxTest.java Changeset: efc977f34464 Author: jgiles Date: 2012-01-27 12:09 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/efc977f34464 RT-19223: [ListView] size of lines in horizontal orientation is too little ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/CellSkinBase.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ListCellSkin.java Changeset: f907f7d6602b Author: jgiles Date: 2012-01-27 12:38 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/f907f7d6602b RT-19108: TableView text is black when selected (instead of being white) ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css Changeset: 93ced8980dbf Author: jgiles Date: 2012-01-27 12:42 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/93ced8980dbf Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From hang.vo at oracle.com Thu Jan 26 16:52:32 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 27 Jan 2012 00:52:32 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-15211 Axis: negative tickLength issue Message-ID: <20120127005232.CFAE2471E8@hg.openjdk.java.net> Changeset: 6bb8c8ad4dc0 Author: Paru Somashekar Date: 2012-01-26 16:47 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6bb8c8ad4dc0 fix RT-15211 Axis: negative tickLength issue ! javafx-ui-controls/src/javafx/scene/chart/Axis.java From hang.vo at oracle.com Thu Jan 26 18:12:28 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 27 Jan 2012 02:12:28 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120127021229.087D0471EB@hg.openjdk.java.net> Changeset: 2b9c1c337dae Author: jgiles Date: 2012-01-27 14:34 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/2b9c1c337dae RT-18536: [TableView] Continuous selection using SHIFT-SPACE in multi cell selection mode does not work as expected ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java ! javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java Changeset: b8886991f516 Author: jgiles Date: 2012-01-27 15:10 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b8886991f516 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From gajasutra at gmail.com Fri Jan 27 06:19:41 2012 From: gajasutra at gmail.com (Gaja Sutra) Date: Fri, 27 Jan 2012 15:19:41 +0100 Subject: Backwards compatibility and auto updates In-Reply-To: References: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> <547264BB-D921-4BFE-9BDA-92B7EB7D2A3B@gmail.com> Message-ID: <4F22B27D.7090908@gmail.com> > My assumption was that if you cobundle FX with your app, then you have control. True, that may also require cobundling the JRE so as not to use the system JRE. But to be honest, I'd do it that way anyhow because the JRE isn't 100% backwards compatible from release to release either. > > What I think would be ideal is for there to be a Sparkle like framework for Java, such that you can get the patch-in place auto-upgrade and so forth for an app written in Java, cobundled with the JRE + FX. If we use a private JRE/JavaFX cobundled, can we reduce size of Java/JavaFX runtime (then global installer) with Proguard + Pack200? Currently, this is not allowed by Oracle Binary Code License: (i) you distribute the Redistributables complete and unmodified, and only bundled as part of Programs, [...] Thanks. NB: When I go at JavaFX documentation and try to read the README for knowing optional parts, like in JRE, I don't find JavaFX 2.0.2 README (only not redistributable 2.0!): http://www.oracle.com/technetwork/java/javase/terms/readme/index.html It would be an useful add, at least for JavaFX 2.1. From nicolas.lorain at oracle.com Fri Jan 27 09:07:31 2012 From: nicolas.lorain at oracle.com (Nicolas Lorain) Date: Fri, 27 Jan 2012 09:07:31 -0800 Subject: Backwards compatibility and auto updates In-Reply-To: <4F22B27D.7090908@gmail.com> References: <3424604B-4B55-448A-802B-2070617AA9CC@oracle.com> <547264BB-D921-4BFE-9BDA-92B7EB7D2A3B@gmail.com> <4F22B27D.7090908@gmail.com> Message-ID: <4F22D9D3.5000705@oracle.com> Hi Gaja, We'll look into both issues. Documenting the optional parts is an obvious oversight that we should be able to fix easily. The former question may require some more time because we will have to involve somebody in our legal department Nicolas On 1/27/12 6:19 AM, Gaja Sutra wrote: > >> My assumption was that if you cobundle FX with your app, then you >> have control. True, that may also require cobundling the JRE so as >> not to use the system JRE. But to be honest, I'd do it that way >> anyhow because the JRE isn't 100% backwards compatible from release >> to release either. >> >> What I think would be ideal is for there to be a Sparkle like >> framework for Java, such that you can get the patch-in place >> auto-upgrade and so forth for an app written in Java, cobundled with >> the JRE + FX. > If we use a private JRE/JavaFX cobundled, can we reduce size of > Java/JavaFX runtime (then global installer) with Proguard + Pack200? > > Currently, this is not allowed by Oracle Binary Code License: > (i) you distribute the Redistributables complete and unmodified, and > only bundled as part of Programs, [...] > > Thanks. > > NB: When I go at JavaFX documentation and try to read the README for > knowing optional parts, like in JRE, I don't find JavaFX 2.0.2 README > (only not redistributable 2.0!): > http://www.oracle.com/technetwork/java/javase/terms/readme/index.html > It would be an useful add, at least for JavaFX 2.1. From hang.vo at oracle.com Fri Jan 27 13:52:30 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Fri, 27 Jan 2012 21:52:30 +0000 Subject: hg: openjfx/2.1/master/rt: RT-18928: TabPane testability issue. There is no way to get menu item in context menu of tab pane corresponding to Tab Message-ID: <20120127215231.32D564723E@hg.openjdk.java.net> Changeset: c54b6cbf31c4 Author: Kinsley Wong Date: 2012-01-27 13:44 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/c54b6cbf31c4 RT-18928: TabPane testability issue. There is no way to get menu item in context menu of tab pane corresponding to Tab ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java From hang.vo at oracle.com Mon Jan 30 16:03:34 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 31 Jan 2012 00:03:34 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120131000336.087DD47293@hg.openjdk.java.net> Changeset: 26250395ab5c Author: Kinsley Wong Date: 2012-01-30 15:59 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/26250395ab5c RT-17224: Fix regression SplitPane: out of bounds divider position issue ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/SplitPaneSkin.java ! javafx-ui-controls/test/javafx/scene/control/SplitPaneTest.java Changeset: 6ea72b395be8 Author: Kinsley Wong Date: 2012-01-30 16:00 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/6ea72b395be8 Merge From hang.vo at oracle.com Mon Jan 30 17:32:56 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 31 Jan 2012 01:32:56 +0000 Subject: hg: openjfx/2.1/master/rt: Fixed RT-18498: Labeled component with multi-lines text throws exception when setting setWrapText(true) and OverrunStyle.CENTER_WORD_ELLIPSIS. Message-ID: <20120131013257.13D1747294@hg.openjdk.java.net> Changeset: 3fc9273c1f80 Author: leifs Date: 2012-01-30 17:29 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/3fc9273c1f80 Fixed RT-18498: Labeled component with multi-lines text throws exception when setting setWrapText(true) and OverrunStyle.CENTER_WORD_ELLIPSIS. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/Utils.java From hang.vo at oracle.com Mon Jan 30 19:52:47 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 31 Jan 2012 03:52:47 +0000 Subject: hg: openjfx/2.1/master/rt: 4 new changesets Message-ID: <20120131035249.1DFAA47298@hg.openjdk.java.net> Changeset: e6ea74639e02 Author: jgiles Date: 2012-01-28 09:48 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/e6ea74639e02 RT-19093: ComboBox preferred size dependent on item selection ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualContainerBase.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java Changeset: 79a0743d0828 Author: jgiles Date: 2012-01-28 10:04 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/79a0743d0828 RT-19059: [ListView] focus index is not updated, when ctrl+click done over selected item (not updated focus index on deselect) ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListCellBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableCellBehavior.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java Changeset: 21fb5e1b075b Author: jgiles Date: 2012-01-31 14:34 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/21fb5e1b075b RT-19310: ListView fails to load skin class (regression observed in 2.1b10) ! javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java Changeset: dac294f2923d Author: jgiles Date: 2012-01-31 16:45 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/dac294f2923d Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From hang.vo at oracle.com Tue Jan 31 00:03:50 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 31 Jan 2012 08:03:50 +0000 Subject: hg: openjfx/2.1/master/rt: fix RT-18258 : Action attached to a menu item of a sub-menu is not Message-ID: <20120131080351.C3D7B472A3@hg.openjdk.java.net> Changeset: d8361fab417f Author: Paru Somashekar Date: 2012-01-31 00:04 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/d8361fab417f fix RT-18258 : Action attached to a menu item of a sub-menu is not called by using the shortcut bound to it. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/MenuButtonSkinBase.java From hang.vo at oracle.com Tue Jan 31 06:42:50 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 31 Jan 2012 14:42:50 +0000 Subject: hg: openjfx/2.1/master/rt: RT-19303 - Accelerator text in menus doesn't support Shortcut as a modifier. Message-ID: <20120131144250.DEF28472B2@hg.openjdk.java.net> Changeset: 5d03caa290c3 Author: mickf Date: 2012-01-31 14:28 +0000 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/5d03caa290c3 RT-19303 - Accelerator text in menus doesn't support Shortcut as a modifier. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/KeystrokeUtils.java ! javafx-ui-controls/test/com/sun/javafx/scene/control/skin/KeystrokeUtilsTest.java From hang.vo at oracle.com Tue Jan 31 07:12:54 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Tue, 31 Jan 2012 15:12:54 +0000 Subject: hg: openjfx/2.1/master/rt: 2 new changesets Message-ID: <20120131151255.11FF2472B3@hg.openjdk.java.net> Changeset: dd487d21afcc Author: hudson Date: 2012-01-25 11:18 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/dd487d21afcc Added tag 2.1-b10 for changeset a1590965f8b4 ! .hgtags Changeset: 7e2f6c7ce2c0 Author: David Grieve Date: 2012-01-31 10:01 -0500 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/7e2f6c7ce2c0 Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/MASTER/jfx/rt From hang.vo at oracle.com Tue Jan 31 16:22:41 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 01 Feb 2012 00:22:41 +0000 Subject: hg: openjfx/2.1/master/rt: 3 new changesets Message-ID: <20120201002242.B3B71472C0@hg.openjdk.java.net> Changeset: efe9f501fce6 Author: jgiles Date: 2012-02-01 12:52 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/efe9f501fce6 RT-19307: Add style class "text" to Text nodes inside control skins. ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledSkinBase.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ProgressIndicatorSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java ! javafx-ui-controls/src/javafx/scene/chart/Axis.java Changeset: b9274656245c Author: jgiles Date: 2012-02-01 13:09 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b9274656245c RT-19341: Remove excessive -fx-font-smoothing-type settings in caspian.css ! javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css Changeset: 0640e91565cc Author: jgiles Date: 2012-02-01 13:11 +1300 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/0640e91565cc Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.1/scrum/controls/jfx/rt From hang.vo at oracle.com Tue Jan 31 22:52:54 2012 From: hang.vo at oracle.com (hang.vo at oracle.com) Date: Wed, 01 Feb 2012 06:52:54 +0000 Subject: hg: openjfx/2.1/master/rt: [Doc only] RT-18252 Link to SelectionModel from ChoiceBox Message-ID: <20120201065255.E45C6472C8@hg.openjdk.java.net> Changeset: b3daae3b5265 Author: Paru Somashekar Date: 2012-01-31 22:46 -0800 URL: http://hg.openjdk.java.net/openjfx/2.1/master/rt/rev/b3daae3b5265 [Doc only] RT-18252 Link to SelectionModel from ChoiceBox ! javafx-ui-controls/src/javafx/scene/control/ChoiceBox.java