This discussion is archived
7 Replies Latest reply: Jan 12, 2010 2:46 PM by 843851 RSS

JavaFX storage based url handler

843851 Newbie
Currently Being Moderated
Hello,

Since media can only be loaded by passing a url string, I have been trying to see if it is possible to create a custom URL handler to load content that cannot be retrieved from normal url protocols.

In this particular scenario, I have tried to create a custom URL protocol designed to load stuff from the JavaFX Storage and Resource classes. Some tests reveal that the media api will try to load with the custom protocol when passed. But I have run into an unusual bug. Does anyone know what this means?:
java.io.IOException: insert failed: file:/video.flv already exixts
        at com.sun.javafx.io.MuffinStore.createMuffinEntry(MuffinStore.java:181)
        at com.sun.javafx.io.StorageImpl$6.run(StorageImpl.java:291)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.io.StorageImpl.create(StorageImpl.java:286)
        at com.sun.javafx.io.FileImpl.create(FileImpl.java:57)
        at com.sun.javafx.io.FileImpl.create(FileImpl.java:50)
        at javafx.io.Storage.postInit$(Storage.fx:122)
        at com.sun.javafx.runtime.FXBase.complete$(FXBase.java:57)
        at sun.net.www.protocol.jstp.StorageClientImpl.openInputStream(StorageClientImpl.fx:20)
        at sun.net.www.protocol.jstp.StorageClientImpl.openInputStream(StorageClientImpl.fx:20)
        at sun.net.www.protocol.jstp.StorageURLConnection.connect(StorageURLConnection.java:48)
        at sun.net.www.protocol.jstp.StorageURLConnection.getInputStream(StorageURLConnection.java:57)
        at java.net.URL.openStream(URL.java:1010)
        at imageviewer.Main.javafx$run$(Main.fx:54)
        at imageviewer.Main.javafx$run$(Main.fx:54)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
70
        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 com.sun.javafx.runtime.provider.GUIRuntimeProvider$1.run(GUIRuntimeProvider.java:65)
        at com.sun.javafx.tk.swing.SwingToolkit$StartupRoutine.run(SwingToolkit.fx:593)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
I can't figure out why it would try to create an entry that already exists. My best guess is some sort of java-javafx incompatability.

Here is the relevant source code:

Main:
//Enable this javafx object to be called by a java object
StorageURLConnection.setStorageClient(StorageClientImpl {});

//Test to ensure that the url can actually open a connection.
var is: InputStream = new URL("jstp:///video.flv").openStream();
FX.println(is.read());
is.close();
Here are the classes that implement the customized url protocol. I put them into the sun.net.www.protocol package for this test since that is the only way to get them to load into the URL's connection factory without a cumbersome command line argument.
//This interface is implemented by a javafx object to ensure it can be called from java
public interface StorageClient {
    InputStream openInputStream(String path);
}
//Retrieves the inputstream from the Storage object for the URLConnection implementation.
public class StorageClientImpl extends StorageClient {
    override public function openInputStream (source: String) : InputStream {
        return Storage {
            source: source
        }.resource.openInputStream();
    }
}
public class StorageURLConnection extends URLConnection {
    static StorageClient client;

    private InputStream inputStream;

    /**
     * Creates an instance of StorageURLConnection.
     * @param url
     */
    public StorageURLConnection(URL url) {
        super(url);
    }

    /**
     * Implement URLConnection.
     */

    /**
     * Connects to javafx's storage.
     */
    @Override
    public synchronized void connect() {
        if (!this.connected) {
            /**
             * Connect to storage.
             * The source is equivelant
             * to the url path.
             */
            inputStream = client.
                openInputStream(url.getPath());
            /* Indicate that this is now connected */
            connected = true;
        }
    }

    @Override
    public InputStream getInputStream() {
        connect();
        return inputStream;
    }

    /**
     * Enables the JavaFX StorageClientImpl
     * class to be called from java without
     * issues from the abstraction layer.
     * @param client
     */
    public static void setStorageClient(StorageClient client) {
        StorageURLConnection.client = client;
    }
}
public class Handler extends URLStreamHandler {
    public Handler() {
        //
    }

    /**
     * Opens a url connection to Storage.
     * @param url
     * @return
     */
    @Override
    protected URLConnection openConnection(URL url) {
        return new StorageURLConnection(url);
    }
    
    /**
     * This type of url does not connect to
     * an external host, so this method merely
     * transfers the request to openConnection(URL url);
     * @param u
     * @param p
     * @return
     * @throws IOException
     */
    @Override
    protected URLConnection openConnection(URL u, Proxy p) throws IOException {
     return openConnection(u);
    }
}
  • 1. Re: JavaFX storage based url handler
    843851 Newbie
    Currently Being Moderated
    Hello aidreamer.

    Did you find any solution to this problem? I am having the same need.
  • 2. Re: JavaFX storage based url handler
    843851 Newbie
    Currently Being Moderated
    Afraid not.

    I even tried the impractical solution of loading the storage file to memory in a byte array first. I then put that byte array into another custom handler called "memory" and tried to read it from there like so:
    Stage {
        width: 500
        height: 500
        scene: Scene {
            content: [
                Button {
                    text: "Play sound!"
                    action: function() {
                        var player: MediaPlayer = MediaPlayer {
                            media: Media {
                                source: "memory:///Engine.mp3"
                            }
                        }
                    }
                }
            ]
        }
    }
    Even then, I just got the following exception:
    FX Media Object caught Exception com.sun.media.jmc.MediaUnsupportedException: Unsupported media: memory:///Engine.mp3
        source ='memory:///Engine.mp3'
    com.sun.media.jmc.MediaUnsupportedException: Unsupported media: memory:///Engine.mp3
            at com.sun.media.jmcimpl.PeerManager$1.run(PeerManager.java:58)
            at java.security.AccessController.doPrivileged(Native Method)
            at com.sun.media.jmcimpl.PeerManager.throwMediaException(PeerManager.java:51)
            at com.sun.media.jmcimpl.PeerManager.getMediaPeer(PeerManager.java:211)
            at com.sun.media.jmc.Media.<init>(Media.java:50)
            at javafx.scene.media.Media.set$source(Media.fx:87)
            at tester.Main$1.lambda(Main.fx:54)
            at tester.Main$1.lambda(Main.fx:54)
            at tester.Main$1.invoke(Main.fx:51)
            at tester.Main$1.invoke(Main.fx:51)
            at tester.Main$1.invoke(Main.fx:51)
            at tester.Main$1.invoke(Main.fx:51)
            at javafx.scene.control.Button.fire(Button.fx:50)
            at com.sun.javafx.scene.control.ButtonBaseBehavior.mouseRelease(ButtonBaseBehavior.fx:78)
            at com.sun.javafx.scene.control.caspian.AbstractButtonSkin$4.lambda(AbstractButtonSkin.fx:68)
            at com.sun.javafx.scene.control.caspian.AbstractButtonSkin$4.invoke(AbstractButtonSkin.fx:68)
            at com.sun.javafx.scene.control.caspian.AbstractButtonSkin$4.invoke(AbstractButtonSkin.fx:68)
            at javafx.scene.Node.impl_processMouseEvent(Node.fx:1682)
            at javafx.scene.Node.preprocessMouseEvent(Node.fx:1708)
            at javafx.scene.Scene$MouseHandler.process(Scene.fx:688)
            at javafx.scene.Scene$MouseHandler.process(Scene.fx:573)
            at javafx.scene.Scene.impl_processMouseEvent(Scene.fx:391)
            at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.fx:519)
            at com.sun.javafx.tk.swing.SwingScene$SwingScenePanel.doMouseEvent(SwingScene.java:388)
            at com.sun.javafx.tk.swing.SwingScene$SwingScenePanel.mouseReleased(SwingScene.java:412)
            at java.awt.Component.processMouseEvent(Component.java:6263)
            at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
            at java.awt.Component.processEvent(Component.java:6028)
            at java.awt.Container.processEvent(Container.java:2041)
            at java.awt.Component.dispatchEventImpl(Component.java:4630)
            at java.awt.Container.dispatchEventImpl(Container.java:2099)
            at java.awt.Component.dispatchEvent(Component.java:4460)
            at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
            at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
            at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
            at java.awt.Container.dispatchEventImpl(Container.java:2085)
            at java.awt.Window.dispatchEventImpl(Window.java:2475)
            at java.awt.Component.dispatchEvent(Component.java:4460)
            at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
            at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
            at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
            at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
            at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
            at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
            at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
    I'm guessing that whatever code lies behind the media implementation does indeed use the URL.openConnection() method, but is only configured to recognize the file types with the standard protocols.
  • 3. Re: JavaFX storage based url handler
    843851 Newbie
    Currently Being Moderated
    Hi again.

    I did som initial tests with the Storage class, and discovered that I got the same error message as you did when I tried to simply create a (local storage) file in the root folder.

    This works fine:
     var entry = Storage {
        source: "myfile.txt"
     }
     println("source = {entry.source}");
    This causes an exception:
     var entry = Storage {
        source: "/mmyfile.txt"
     }
     println("source = {entry.source}");
    =>
     java.io.IOException: insert failed: file:/mmyfile.txt already exixts
            at com.sun.javafx.io.MuffinStore.createMuffinEntry(MuffinStore.java:181)
            at com.sun.javafx.io.StorageImpl$6.run(StorageImpl.java:291)
            at java.security.AccessController.doPrivileged(Native Method)
            at com.sun.javafx.io.StorageImpl.create(StorageImpl.java:286)
            at com.sun.javafx.io.FileImpl.create(FileImpl.java:57)
            at com.sun.javafx.io.FileImpl.create(FileImpl.java:50)
            at javafx.io.Storage.postInit$(Storage.fx:122)
            at com.sun.javafx.runtime.FXBase.complete$(FXBase.java:57)
            at se.explodera.bokstavera.StorageTest.javafx$run$(StorageTest.fx:15)
            at se.explodera.bokstavera.StorageTest.javafx$run$(StorageTest.fx:15)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
            at java.lang.reflect.Method.invoke(Unknown Source)
            at com.sun.javafx.runtime.provider.GUIRuntimeProvider$1.run(GUIRuntimeProvider.java:65)
            at com.sun.javafx.tk.swing.SwingToolkit$StartupRoutine.run(SwingToolkit.fx:593)
            at java.awt.event.InvocationEvent.dispatch(Unknown Source)
            at java.awt.EventQueue.dispatchEvent(Unknown Source)
            at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
            at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
            at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
            at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
            at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
            at java.awt.EventDispatchThread.run(Unknown Source)
    When I look at your code, it seems like you have tried putting the local storage media file in the root folder.
    var is: InputStream = new URL("jstp:///video.flv").openStream();
    Could you try putting it in a more "local" folder, as described in the [API documentation for Storage|http://java.sun.com/javafx/1.2/docs/api/javafx.io/javafx.io.Storage.html] ?
  • 4. Re: JavaFX storage based url handler
    843851 Newbie
    Currently Being Moderated
    I tried putting the video file in a jar file and give the jar url to the MediaPlayer, without success. "MediaUnsupportedException".

    MagnusG
  • 5. Re: JavaFX storage based url handler
    843851 Newbie
    Currently Being Moderated
    The documentation states that loading video from jar files is not supported to discourage doing exactly that. This is because video is already a highly compressed format, making the process of putting it in a jar file highly inefficient.

    Still, it would be nice if there were more flexible ways of loading content than merely loading the url protocol formats supported by default.
  • 6. Re: JavaFX storage based url handler
    843851 Newbie
    Currently Being Moderated
    Well, the performance argument is rather weak -- there is no need to compress the jar file if you do not want to.

    I have a library of video clips that I want to include in my app, and it would have been very convenient -- for me as well as for the user -- to be able to put them all in a jar file. This "protocol restriction" forces me to make a zip file with everything and instruct the user to download, unpack and install/doubleclick. The whole web start thing becomes rather useless. Sigh. Well, I guess I can add som download manager to the application, fetch all the files and store them locally. If that is allowed...

    Plus, in my other app the video format restrictions will probably force me to build separate versions for Windows (using QuickTime for Java) and Macintosh (using JavaFX).

    Sun has still got some work to do before Java/JavaFX handles video in an acceptable way.

    Anyway, thanks for all the help. I saved a lot of time.

    MagnusG
  • 7. Re: JavaFX storage based url handler
    843851 Newbie
    Currently Being Moderated
    It would require signing if you are doing this in an applet, but its pretty easy to automate the process of unzipping the files.

    You just need to figure out some platform independent way of determining were to put them once unzipped. You could put them into folder in the home directory (using system properties to figure where that is on the present system) or ask the user themself to select where to store them and save the url to the directory with the Preferences API.