`lein run` VS `lein uberjar` behaviour

89 views
Skip to first unread message

dimitris

unread,
Feb 18, 2018, 8:13:36 AM2/18/18
to Clojure
Hi all,

I'm experiencing something very strange with my JavaFX clojure
application. What the application actually is about is not super
important. What is important is the fact that I'm having a different
experience when I run it via `lein run` (or in fact `lein trampoline
run`), than when I run the created uberjar (via `lein uberjar`).

The difference is the following:

In my `-start` method I'm creating a MediaPlayer object (with a .wav
resource), and setting its cycleCount to INDEFINITE, which is to say
that is will keep repeating the audio file forever. That's all fine and
dandy and it does work as expected when run through lein. However, the
exact same code in the uberjar plays the file once and then it stops!
The game carries on as usual with no exceptions or anything like
that...It's only the music that stops!

Now, before someone says that the MediaPlayer object has been GC'ed (as
indicated by [1], [2] & [3]), I should point out that I am able to
restart the music (by pressing 'M)' after it has stopped and while the
program is running normally. The only thing that the KeyCode handler for
M is doing calling `.play` or `.stop` on the original MediaPlayer object
i created at the very start. In other words, I'm not creating any new
MediaPlayer objects anywhere else other than the `-start` method. Unless
I'm missing something, i shouldn't be able to restart the music if the
MediaPlayer object had been GC'ed, right?

I'm not sure if it plays any role but the MediaPlayer object lives
inside a promise, which again is delivered in the `-start` method, right
before the javaFX Scene is shown. So the controls that need access to
that MediaPlayer object (like the <M> handler mentioned earlier), go
through that promise to get it. I only did that because i want to have
it as a global in that ns, but at the same time i can't just `def` it
because the javaFX toolkit is not initialised at compile time. So it's
either going to be a `promise` or a `delay`. In any case, I'm really not
sure how/why this would be an issue - I'm just throwing it out there as
an 'interesting' complication in case I'm wrong.

Other than that, everything seems to work as expected, which is the same
as with `lein run`. Feel free to drop suggestions, and/or brainstorm
ideas. I could even provide the project in a zip if you would like to
experience it yourself.

Thanks in advance...

Kind regards,

Dimitris

[1]:
https://stackoverflow.com/questions/29870368/javafx-mediaplayer-music-stops-after-10-seconds

[2]:
https://stackoverflow.com/questions/6241687/mediaplayer-stop-playing-after-about-5-seconds

[3]:
https://stackoverflow.com/questions/30789973/javafx-mediaplayer-stops-when-file-isnt-complete

Mine stops after the first cycle every time (which is between 15-16 seconds)





Matching Socks

unread,
Feb 18, 2018, 2:46:32 PM2/18/18
to Clojure
With the luxury of commenting on complex software sight-unseen... best of all, JavaFX... and you mention an uberjar, which grants opportunity to vent baseless suspicions about class-loading and compilation differences... could it be that the MediaPlayer is being instantiated *twice* in the uberjar version of the program?  The first instance of the MediaPlayer lasts only a few milliseconds (during which it starts playing), then it is supplanted by the second instance, whereupon instance No.1 becomes eligible for garbage collection.  When someone presses 'M' it's the second MediaPlayer that plays.  

Sean Corfield

unread,
Feb 18, 2018, 10:10:33 PM2/18/18
to clo...@googlegroups.com

Show us some of your code.

 

Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

 


From: clo...@googlegroups.com <clo...@googlegroups.com> on behalf of dimitris <jimpi...@gmail.com>
Sent: Sunday, February 18, 2018 5:13:06 AM
To: Clojure
Subject: `lein run` VS `lein uberjar` behaviour
 
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

dimitris

unread,
Feb 19, 2018, 11:53:03 AM2/19/18
to clo...@googlegroups.com

Ok, so here is the ns declaration:

(ns msnake.gui
  (:require ...the core game namespaces)
  (:import ...a bunch of JavaFX classes)
  (:gen-class :extends javafx.application.Application))

And here is the `-start` method and the animation-timer in case anyone is wondering what that is:

(defn- animation-timer
  ^AnimationTimer [proceed-game!]
  (let [last-update (SimpleLongProperty. 0)]
    (proxy [AnimationTimer] []
      (handle [now]
        (let [last-update* (.get last-update)
              nanos-elapsed (- now last-update*)]
          (when (>= nanos-elapsed @ctrl/turn-nanos)
            (proceed-game!)
            (.set last-update now)))))))

(defn -start [app ^Stage stage]
  (let [player-no (new-game-dialog!)
        entities (ctrl/game-entities player-no)
        canvas (doto (Canvas. 750 500)
                 (.setFocusTraversable true)
                 (.setOnKeyPressed (fxut/event-handler [e]
                                     (apply react! e entities))))
        root (StackPane. (into-array [canvas]))
        context (.getGraphicsContext2D canvas)
        scene (Scene. root 750 500)
        timer (animation-timer (partial paint/paint-all context entities))
        audio (doto (MediaPlayer. (-> "PimPoy.wav" io/resource .toString (Media.)))
                (.setVolume start-volume)
                (.setAutoPlay true)
                (.setCycleCount MediaPlayer/INDEFINITE))]
    (deliver start-gui-loop #(.start timer))
    (deliver audio-engine audio)
    (doto stage
      (.setResizable false)
      (.setTitle "MX SNAKE")
      (.setOnCloseRequest (fxut/event-handler [_] (System/exit 0)))
      (.setScene scene)
      (.show))

    (fxut/run-later* @start-gui-loop)))

As you can see the MediaPlayer object is created once. It is accessed through various controllers (volume+/- etc) but never created anew. For example here is the handler for <M>:

KeyCode/M     (fn [& _]
                (if (= MediaPlayer$Status/PLAYING (.getStatus @audio-engine))
                  (.stop @audio-engine)
                  (.play @@audio-engine)))

Here is main.clj:

(ns msnake.main
  (:import (javafx.application Application)
           (msnake gui))
  (:gen-class :main true))

(defn -main [& args]
  (Application/launch gui (into-array String args)))


and finally project.clj

(defproject msnake "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.9.0"]]
  :aot [msnake.gui msnake.paint msnake.main]
  :main msnake.main
  :jvm-opts ["-Dclojure.compiler.direct-linking=true"]
  )

I should point out that direct-linking is not the culprit here. Exactly the same thing happens with or without direct-linking :(.

As you can see I'm playing by the book - i.e. subclassing Application, and launching through the overridden `-start` method.

Kind regards,

Dimitris

dimitris

unread,
Feb 19, 2018, 12:11:24 PM2/19/18
to clo...@googlegroups.com

Darn it, i made a typo copying and it may throw some people off. There is a double-deref `@@` in the <M> handler i pasted. Apologies for the slopiness... :(

Regards,

Dimitris

Reply all
Reply to author
Forward
0 new messages