Applet Image Loading

What is the best way to load images and XML data to a java applet?

What I have at the moment is an Slick2D Game applet. I have a content loader from an application project that takes an XML file containing links to the games separate image/sound/particles, etc that need to be loaded.

Finally I have a directory called ‘art’ that contains the actual image files.

On my server I have kept the same directory structure as the application version:

MyApplet
- data
- resources.xml
- art
- image1.png
- image2.png
- image3.png
- image4.png

When running the applet, I get the following error:


Exception in thread "Thread-14" java.lang.ExceptionInInitializerError
	at DecoGame_NixieClock.NixieClockGame.init(NixieClockGame.java:43)
	at org.newdawn.slick.AppletGameContainer$Container.initApplet(AppletGameContainer.java:272)
	at org.newdawn.slick.AppletGameContainer$ContainerPanel.initGL(AppletGameContainer.java:229)
	at org.newdawn.slick.AppletGameContainer$ContainerPanel.start(AppletGameContainer.java:216)
	at org.newdawn.slick.AppletGameContainer$1.run(AppletGameContainer.java:92)
Caused by: java.security.AccessControlException: access denied (java.io.FilePermission data\resources.xml read)
	at java.security.AccessControlContext.checkPermission(Unknown Source)
	at java.security.AccessController.checkPermission(Unknown Source)
	at java.lang.SecurityManager.checkPermission(Unknown Source)
	at java.lang.SecurityManager.checkRead(Unknown Source)
	at java.io.FileInputStream.<init>(Unknown Source)
	at Content.ResourceManager.<init>(ResourceManager.java:50)
	at Content.ResourceManager.<clinit>(ResourceManager.java:33)
	... 5 more

If I’m right, this is because applets are not allowed to access the local file-system.
Is there a method for loading data to an applet from the server?

This is the layout of the XML file I use to store my content links:


<?xml version="1.0" encoding="UTF-8"?>
<resources>
<!-- GAMEPLAY -->

  <!-- GUI -->
  <resource type="image" id="IMAGE_1">art\image1.png</resource>
  <resource type="image" id="IMAGE_2">art\image2.png</resource>
  <resource type="image" id="IMAGE_3">art\image3.png</resource>

</resources>

Would it just be a matter of changing these links to say ‘www.mydomain.com/applet/art/image1.png’ or is there a better way?

Instead of using java.io.File stuff to load your resources, you should be using java.lang.ClassLoader’s getResource methods.
These will search the classpath and return a stream from which you can load your xml file and images. This works for applications and applets alike, you just need to make sure that your resource files are on the classpath: for applications you add the resource directory to the classpath, for applets you package the resources into a jar file and include it just like a code jar.

Ok, I’ll give that a go.

Is it possible in NetBeans to include folders and images into the class path, or only java class files?
Just now I’m adding them in after build with 7-zip … which I guess is a bit silly.

You can also use Java.io.File if you pass in a URL instead of a local file, then you can pass in your server’s URL. This, however, is quite slow and not something I would do unless you want to be swapping just this image out frequently or something. Instead use the classpath.

Not sure how it works in Netbeans, but in Eclipse there’s a “Classpath” tab in the run configuration dialog that lets you add directories. From the command line you just use the -cp option.

Hi,

I’ve had a go at modifying my resource manager class to use ClassLoader (rather than fileIo). I’m having a lot of trouble understanding how this is meant to work. Does anyone know of any basic tutorials that cover loading files this way? Everything I’ve been able to find seems to only work with actual classes.

Here’s the code I have tried for reference:


public class ResourceManager {

	private static ResourceManager _instance = new ResourceManager();
        private static ClassLoader classLoader;

	private Map<String, Sound> soundMap;
	private Map<String, Image> imageMap;
	private Map<String, ResourceAnimationData> animationMap;
	private Map<String, String> textMap;
        private Map<String, ParticleSystem> particleMap;

	private ResourceManager() {
		soundMap = new HashMap<String, Sound>();
		imageMap = new HashMap<String, Image>();
		animationMap = new HashMap<String, ResourceAnimationData>();
		textMap = new HashMap<String, String>();
                particleMap = new HashMap<String, ParticleSystem>();

                classLoader = ClassLoader.getSystemClassLoader();

                try
                {
                    /* NTS:
                     * Modifying this class to load resources from the classPath
                     * so that it can be used within applets.
                     * 22 July 2010, Matt
                     */
                    URL url = classLoader.getResource("assets/data/resources");
                    if(url != null)
                    {
                        File f = new File(url.getPath());
                        this.loadResources(new FileInputStream(f));
                    }
                    else
                    {
                        throw new NullPointerException();
                    }
                }
                catch(SlickException e)
                {
                    e.printStackTrace();
                }
                catch(FileNotFoundException e)
                {
                    e.printStackTrace();
                }
	}

	public final static ResourceManager getInstance(){
		return _instance;
	}

	public void loadResources(InputStream is) throws SlickException {
		loadResources(is, false);
	}

	public void loadResources(InputStream is, boolean deferred) throws SlickException
        {
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = null;
            try
            {
                docBuilder = docBuilderFactory.newDocumentBuilder();
            }
            catch (ParserConfigurationException e)
            {
                throw new SlickException("Could not load resources", e);
            }

            Document doc = null;

            try
            {
                doc = docBuilder.parse (is);
            }
            catch(SAXException e)
            {
                throw new SlickException("Could not load resources", e);
            }
            catch (IOException e)
            {
                throw new SlickException("Could not load resources", e);
            }

		// normalize text representation
            doc.getDocumentElement ().normalize ();

            NodeList listResources = doc.getElementsByTagName("resource");

            int totalResources = listResources.getLength();

            if(deferred)
            {
        	LoadingList.setDeferredLoading(true);
            }


            for(int resourceIdx = 0; resourceIdx < totalResources; resourceIdx++)
            {

        	Node resourceNode = listResources.item(resourceIdx);

        	if(resourceNode.getNodeType() == Node.ELEMENT_NODE){
                    Element resourceElement = (Element)resourceNode;

                    String type = resourceElement.getAttribute("type");

                    if(type.equals("image")){
                            addElementAsImage(resourceElement);
                    }else if(type.equals("sound")){
                            //addElementAsSound(resourceElement);
                    }else if(type.equals("text")){
                            //addElementAsText(resourceElement);
                    }else if(type.equals("font")){

                    }else if(type.equals("animation")){
                           // addElementAsAnimation(resourceElement);
                    }
                    else if(type.equals("particleSystem")){
                            addElementAsParticleSystem(resourceElement);
                    }
        	}
            }
	}

       ... loads of methods for getting the files from the XML nodes ...

Where I’m having trouble is here:


                     /* NTS:
                     * Modifying this class to load resources from the classPath
                     * so that it can be used within applets.
                     * 22 July 2010, Matt
                     */
                    URL url = classLoader.getResource("assets/data/resources.xml");
                    if(url != null)
                    {
                        File f = new File(url.getPath());
                        this.loadResources(new FileInputStream(f));
                    } 
                    else
                    {
                        throw new NullPointerException();
                    }

Which always throws the nullPopinterException with ‘url’ always returning null.

This is all rater new to me, so thanks for being patient :slight_smile:

A couple of things to try:

  • Use the context classloader: Thread.currentThread().getContextClassLoader().
  • Ensure that your paths are correct. Take a look into your resource jar with a zip tool and verify that “assets/data/resources.xml” actually exists.
  • Unrelated to the issue you’re having right now, but there’s no need to use the File and FileInputStream classes - the classloader can return a stream to load from directly with getResourceAsStream()

Also: there’s no need to manually throw a NullPointerException - it’ll be done for you when you call .getPath() on the null reference.

ok, i’ll have a read-up on those.

Would it be better to continue using my hash-tables to store my resources in memory, or to change them to using something like HashTable<String name, String path> and using getResource(path) each time I want to get a resource?

(I assume this would then either load the file from the classpath, or try findLoadedClass() first to get it from the cache, right?)

Definitely load the resources once and keep them around, as you have been doing.

Thanks ryanm!

Loading images this way seems to be working now, and I’ve set-up my game template project to include an assets.art, assets.data, assets.art.sound and assets.art.music class-path, and also a template resources.xml document to load in some default images.

I’ve used this to move my template project over to using Slick’s StateBasedGame class and added an Intro state that show’s my (WIP) blog-site logo with a nice fade-in fade-out effect :slight_smile:

I actually feel like I’ve got something done today.

Code:
public Image loadResource(String id, String path) {

    try
    {
        if(path == null || path.length() == 0)
            throw new SlickException("Image resource [" + id + "] has invalid path");

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Image image = null;

        /* NTS:
         * Modifying this class to load resources from the classPath
         * so that it can be used within applets.
         * 22 July 2010, Matt
         */
        InputStream str = classLoader.getResourceAsStream(path);

        image = new Image(str, id, false);

        System.out.println("Loaded Image "+ id + ": " + path);
       
        ResourceManager.getInstance().PutObject(id, image); // store the loaded image in a hashTable for later use.
       
        return image;
    }

    catch( ... ) { ... }

}

Here is the stack trace i’m getting;

Code:
Loaded Image IMAGE_GAMEWORKS_LOGO: assets/art/rbgwLogo.png
Exception in thread “main” java.lang.ExceptionInInitializerError
at slickgametemplate.RBGWIntroState.init(RBGWIntroState.java:56)
at slickgametemplate.SlickGame.initStatesList(SlickGame.java:39)
at org.newdawn.slick.state.StateBasedGame.init(StateBasedGame.java:164)
at org.newdawn.slick.AppGameContainer.setup(AppGameContainer.java:390)
at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:314)
at slickgametemplate.Main.main(Main.java:33)
Caused by: java.lang.NullPointerException
at content.loaders.ImageLoader.loadResource(ImageLoader.java:47)
at content.ResourceManager.loadImage(ResourceManager.java:211)
at content.ResourceManager.loadImage(ResourceManager.java:206)
at content.ResourceManager.loadResources(ResourceManager.java:181)
at content.ResourceManager.loadResources(ResourceManager.java:126)
at content.ResourceManager.(ResourceManager.java:80)
at content.ResourceManager.(ResourceManager.java:46)
… 6 more
Java Result: 1

I’ve run a few breakpoints, and it seems that the Image being created from the InputStream has zero dimensions (the actual .png is 640*480)

I’ve checked everything I can think of, but got nothing. Any ideas what is happening here?

Thanks,
Matt

I made a simple image loader class like you described, it could easily be modified to do other file types. http://www.java-gaming.org/index.php/topic,20634.0.html hope it helps.

Ahh, that looks cool Zoto, I’ll give it a go.

The method I’ve been using is to load everything into a HashTable at start up, instead of loading on demand, which is what your code does. Do you know if there is any advantage to either solution?

If you load the same image twice, it will load it from disk the first time, every other time the preloaded image is returned.

To pre-load at the beginning just call the function and don’t worry about the return value, when you load it later it will already be in the hash map.

Remember I posted that code to ask a specific question so it’s stripped down, if you use it you will probably want to add some public wrapper functions to at least remove images and clear the hash map.

The trade offs with pre-loading is just the normal ram usage v/s access speed.
Reading from the hard drive will take a relatively long time but ram is a limited resource, you just need to find the right balance for the game your making.

I believe applets have 64MB of ram by default and can only be changed client side, but web start can be set in the jnlp file.

I did not know that. Useful to know though, I think you may just have saved me days of ‘why, WHY is it not working!?’ time in a few months :slight_smile:

you can allocate the memory for an applet by using the following parameter in your applet tag

<param name="java_arguments" value="-Xmx128m">