title: Strange JavaFX Application Thread exception
body: <p>We are making a card game named Dominion with client-server, using Java Serialization. We are working also with observer/observable and threads (threads for reading messages on server and client side).</p>
  
  <p>We are highlighting the card with CSS if a player is able to play or buy a card. The highlight is added by the update methode of the interface Observer where a gamecard is being observed. If a specific condition is true, we will add the highlight to the gamecard (class GameCard is implements Observer).</p>
  
  <p>In general it works fine, but there is a card (named Militia) which forces the opponents to discard cards from their hand until they got only 3 cards left. We can play this for several times without exceptions, but suddenly there are several exceptions (after about 7-10 times played the Militia cards). We have no idea why.</p>
  
  <p>What could be the reason of this exceptions? I think it doesn't help if I post the code, I will post the exceptions:</p>
  
  <pre><code>Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
      at com.sun.javafx.text.PrismTextLayout.addTextRun(PrismTextLayout.java:755)
      at com.sun.javafx.text.GlyphLayout.addTextRun(GlyphLayout.java:140)
      at com.sun.javafx.text.GlyphLayout.breakRuns(GlyphLayout.java:210)
      at com.sun.javafx.text.PrismTextLayout.buildRuns(PrismTextLayout.java:770)
      at com.sun.javafx.text.PrismTextLayout.layout(PrismTextLayout.java:1021)
      at com.sun.javafx.text.PrismTextLayout.ensureLayout(PrismTextLayout.java:223)
      at com.sun.javafx.text.PrismTextLayout.getBounds(PrismTextLayout.java:246)
      at javafx.scene.text.Text.getLogicalBounds(Text.java:358)
      at javafx.scene.text.Text.impl_computeGeomBounds(Text.java:1168)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1524)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.updateCachedBounds(Parent.java:1596)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1524)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1524)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.computeLocalBounds(Node.java:3595)
      at javafx.scene.Node.updateLocalBounds(Node.java:3625)
      at javafx.scene.Node.getLocalBounds(Node.java:3486)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.updateCachedBounds(Parent.java:1596)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1524)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1524)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.updateCachedBounds(Parent.java:1596)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1524)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.updateTxBounds(Node.java:3643)
      at javafx.scene.Node.getTransformedBounds(Node.java:3426)
      at javafx.scene.Parent.getChildTransformedBounds(Parent.java:1732)
      at javafx.scene.Parent.updateCachedBounds(Parent.java:1596)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.impl_intersectsBounds(Node.java:5015)
      at javafx.scene.layout.Region.impl_pickNodeLocal(Region.java:2931)
      at javafx.scene.Node.impl_pickNode(Node.java:4914)
      at javafx.scene.Scene$MouseHandler.pickNode(Scene.java:3899)
      at javafx.scene.Scene$MouseHandler.access$1600(Scene.java:3485)
      at javafx.scene.Scene.pick(Scene.java:1942)
      at javafx.scene.Scene.access$6700(Scene.java:159)
      at javafx.scene.Scene$MouseHandler.process(Scene.java:3711)
      at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
      at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
      at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$353(GlassViewEventHandler.java:417)
      at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
      at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
      at com.sun.glass.ui.View.notifyMouse(View.java:937)
      at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
      at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
      at java.lang.Thread.run(Unknown Source)
  Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: -1
      at java.util.ArrayList.elementData(Unknown Source)
      at java.util.ArrayList.get(Unknown Source)
      at com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
      at com.sun.javafx.collections.VetoableListDecorator.get(VetoableListDecorator.java:306)
      at javafx.scene.Parent.updateCachedBounds(Parent.java:1591)
      at javafx.scene.Parent.recomputeBounds(Parent.java:1535)
      at javafx.scene.Parent.impl_computeGeomBounds(Parent.java:1388)
      at javafx.scene.layout.Region.impl_computeGeomBounds(Region.java:3078)
      at javafx.scene.Node.updateGeomBounds(Node.java:3579)
      at javafx.scene.Node.getGeomBounds(Node.java:3532)
      at javafx.scene.Node.getLocalBounds(Node.java:3480)
      at javafx.scene.Node.impl_intersectsBounds(Node.java:5015)
      at javafx.scene.layout.Region.impl_pickNodeLocal(Region.java:2931)
      at javafx.scene.Node.impl_pickNode(Node.java:4914)
      at javafx.scene.Scene$MouseHandler.pickNode(Scene.java:3899)
      at javafx.scene.Scene$MouseHandler.access$1600(Scene.java:3485)
      at javafx.scene.Scene.pick(Scene.java:1942)
      at javafx.scene.Scene.access$6700(Scene.java:159)
      at javafx.scene.Scene$MouseHandler.process(Scene.java:3711)
      at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
      at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
      at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
      at java.security.AccessController.doPrivileged(Native Method)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$353(GlassViewEventHandler.java:417)
      at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
      at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
      at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
      at com.sun.glass.ui.View.notifyMouse(View.java:937)
      at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
      at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
      at java.lang.Thread.run(Unknown Source)
  Exception in thread "JavaFX Application Thread" 
  </code></pre>
  
  <p><strong>...and if we just mouse hover over the stage, it throws hundreds of times this exception:</strong></p>
  
  <pre><code>Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException
  </code></pre>
  
  <p>Code which could cause the exceptions:
  1. in this class we highlight the cards which the opponent has to discard (until he has left 3 cards; see line where croupier.isDiscardModeMilitia....)</p>
  
  <pre><code>public class GameCard extends Button implements Observer  {
      protected static final boolean MoneyCard = false;
      ServiceLocatorClient sl = ServiceLocatorClient.getServiceLocator();
      Croupier croupier = Croupier.getCroupier();
  
      Label   lbl_cardName;
      String text_DE;
      boolean holeCard  = false;
  
      int costs;
  
      StackSizeInfo stackSizeInfo;
  
  
      public GameCard(Label cardName, String text_DE) {
          super();
          this.lbl_cardName = cardName;
          this.text_DE=text_DE;
          this.assignPicture();
      }
  
      public GameCard(){
      }
  
      GameCard gc = this;
  
      @Override
      public void update(Observable arg0, Object arg1) {
  
          getStyleClass().remove("highlight");
          getStyleClass().remove("highlight2");
          getStyleClass().remove("highlight3");
  
          //Highlighte alle Karten im Kaufmodus, welche ich mit der aktuelln Buypower und buys kaufen kann
          if (croupier.isBuyMode() == true &amp;&amp; croupier.getBuyPower() &gt;= this.costs &amp;&amp; !this.isHoleCard() &amp;&amp; croupier.getBuys() &gt; 0){
              this.getStyleClass().add("highlight");
          }
  
          //Highlighte alle Geldkarten im Kaufmodus
          if (croupier.isBuyMode() == true &amp;&amp; this.isHoleCard() &amp;&amp; this instanceof MoneyCard){
              this.getStyleClass().add("highlight");
          }
  
          //Highlighte alle Aktionskarten im Aktionsmodus
          if (croupier.isActionMode() == true &amp;&amp; this.isHoleCard() &amp;&amp; this instanceof ActionCard){
              this.getStyleClass().add("highlight");
          }
  
          //Highlighte alle HoleCards im Discardmodus (basement)
          if (croupier.isDiscardMode() == true &amp;&amp; this.isHoleCard()){
              this.getStyleClass().add("highlight3");
          }
  
          //Highlighte alle HoleCards im TrashModus Kapelle
          if (croupier.isTrashModeChapel() == true &amp;&amp; this.isHoleCard()){
              this.getStyleClass().add("highlight3");
          }
  
          //Highlighte alle MoneyCards im TrashModus Mine
          if (croupier.isTrashModeMine() == true &amp;&amp; this instanceof MoneyCard &amp;&amp; this.isHoleCard()){
              this.getStyleClass().add("highlight3");
          }
  
          //Highlighte alle Karten auf der Hand, welche im Umbaumodus weggeworfen werden können
          if (croupier.isTrashModeRebuilding() == true &amp;&amp; this.isHoleCard()){
              this.getStyleClass().add("highlight3");
          }
  
          //Highlighte alle Kupferkarten auf der Hand, die gegen +3Geld weggeworfen werden können (ActionCard Geldverleiher)
          if (croupier.isTrashModeMoneylender() == true &amp;&amp; this.isHoleCard() &amp;&amp; this.getLbl_cardName().getText().equals("copper")){
              this.getStyleClass().add("highlight3");
          }
  
          //Highlighte alle MoneyCards, welche erworben werden können im Minen-Modus
          if (croupier.isModeForMine() == true &amp;&amp; this instanceof MoneyCard &amp;&amp; !this.isHoleCard() &amp;&amp; this.costs &lt;= croupier.getSavedMCValueForMineMode()){
              this.getStyleClass().add("highlight");
          }
  
          //Highlighte alle Karten zum Kauf, welche erworben werden können im Rebuilding-Modus
          if (croupier.isModeForRebuilding() == true &amp;&amp; !this.isHoleCard() &amp;&amp; this.costs &lt;= croupier.getCardValueForRebuildingMode()){
              this.getStyleClass().add("highlight");
          }
  
          //Highlighte alle Karten zum Kauf, welche erworben werden können im Rebuilding-Modus
          if (croupier.isModeForWorkshop() == true &amp;&amp; !this.isHoleCard() &amp;&amp; this.costs &lt;= 4){
              this.getStyleClass().add("highlight3");
          }
  
          //Highlighte alle Moat-Karten wenn ein Angriff gemacht worden ist
          if (croupier.isReactionMode() == true &amp;&amp; this.isHoleCard() &amp;&amp; this.getLbl_cardName().getText().equals("moat")){
              this.getStyleClass().add("highlight3");
          }
  
          //Highlighte alle Moat-Karten wenn ein Angriff gemacht worden ist
          if (croupier.isDiscardModeMilitia() == true &amp;&amp; this.isHoleCard()){
              this.getStyleClass().add("highlight2");
          }
  
          //Highlighte alle Moat-Karten wenn ein Angriff gemacht worden ist
          if (croupier.isModeForCurseCard() == true &amp;&amp; !this.isHoleCard() &amp;&amp; this.getLbl_cardName().getText().equals("curse")){
              this.getStyleClass().add("highlight2");
          }
  
          //wenn stacksize auf 0, wird highlighting ebenfalls deaktiviert
          if (croupier.getStackSize(gc) == 0) {
              this.getStyleClass().remove("highlight");
          }
  
          //update das STackSize infoLabel auf der Karte      
          if (this.stackSizeInfo != null){
  
              stackSizeInfo.updateStackSizeInfo();
  
          }
  
  
      }
  
  
  
  
      public void assignStackSizeInfo(){
          stackSizeInfo = new StackSizeInfo(gc,croupier.getStackSize(gc));
      }
  
  
  
  
      public void assignPicture(){
          if (this.holeCard == false)
              this.getStyleClass().addAll("card_mini",lbl_cardName.getText());
          else 
              this.getStyleClass().addAll("card",lbl_cardName.getText()+"_big");
      }
  
  
  
  
      public boolean isHoleCard() {
          return holeCard;
      }
  
      public void setHoleCard(boolean holeCard) {
          this.holeCard = holeCard;
      }
  
      public Label getLbl_cardName() {
          return lbl_cardName;
      }
  
      public String getText_DE(){
          return this.text_DE;
      }
  
      public void setLbl_cardName(Label lbl_cardName) {
          this.lbl_cardName = lbl_cardName;
      }   
  }
  </code></pre>
  
  <ol start="2">
  <li><p>possible code fragment: here we are updating the GUI. This happens whenever we play a card from the hand. We will remove it first from the LinkedList (HoleCards) in another method and then we call updateGUI</p>
  
  <p>public void updateGUI() {</p>
  
  <pre><code>        // Zeichne holeCards Neu
          hb_wrapper_holeCards.getChildren().clear();
          for (int i = 0; i &lt; croupier.getHoleCards().size(); i++) {
              GameCard gc1 = croupier.getHoleCards().get(i);
  
              try{
                  hb_wrapper_holeCards.getChildren().add(gc1);
                  gc1.setPrefSize(160, 260);
                  gc1.setMaxWidth(160);
              }catch (NullPointerException e){
                  //
              }
  
  
          // Zeichne Ablagekarte neu
          hb_wrapper_stapel.getChildren().clear();
          dummyCardAblagestapel.getStyleClass().clear();
          try {
              String cssSelector = new String(croupier.getAblagestapel().peekLast().getLbl_cardName().getText()+"_big");
  
              dummyCardAblagestapel.getStyleClass().addAll("card",cssSelector);
              hb_wrapper_stapel.getChildren().addAll(dummyCardAblagestapel,nachziehstapel);   
          } catch(Exception e){
              hb_wrapper_stapel.getChildren().addAll(this.dummyCardAblagestapel,nachziehstapel);  
              }           
          }
  
      }
  </code></pre></li>
  </ol>
  
  <p><strong>note1: I guess it happens in the first fragment where we highlight the cards. After a player has discarded cards until he has only 3 left in the hand, we will remove the highlight. Exactly when he discards his last card, the exception is thrown (but as I said, we can play this for about 7-10 times without exceptions, but suddenly the exceptions are thrown)</strong></p>
  
  <p><strong>note2: I have already tried with Platform.runLater on this 2 code fragments and it didn't help</strong></p>