pack200 applet

That’s pretty much what I’m getting. If I leave the jar in the same directory, then the browser goes and gets that Ok. On the positive note, my hosting provider is using Apache, so hopefully I can fix up a server solution using .htaccess. However I think that’s a project for the weekend.

I have not been successful to use pack200 locally…

I have thought, but not investigated, about directly using game.pack.gz as instead of the game.jar in the applet tag, but append appropriate html response (with pack 200 mime type) message to the front of game.pack.gz to try and trick the JVM in to thinking it recieved a pack 200 archive from a server.

@pjt33

can you try instead? (without the .jar bit)

Perhaps it requires a server sending a “Content-Encoding: pack200-gzip” header.

Grr… .I’ve been trying to accomplish this same thing, run an pack200 applet using the appletviewer, locally, but no luck. Why oh why does Sun need to make technologies such a pain to use client-side?

Same error.

I won’t claim it’s pretty, but I don’t think there are enough security flaws in this for anyone to do anything nasty to your machine. Tested and it allowed me to play the .pack.gz of Gravitational Fourks; the logging output is good enough to check that it is getting the file you think it is. I haven’t played around with caching pragmas, because I want to get on with actually developing a game for this year, but I suspect they would be helpful.

import java.io.*;
import java.net.*;
import java.util.*;

/**
 * A hacky HTTP daemon. Not designed for heavyweight usage; incorporates basic
 * security, but nothing fancy; does support .pack.gz (so useful for Java4k
 * testing).
 * @author pjt33
 */
public class HTTPD implements Runnable
{
	private static File rootDir;

	private final Socket sock;

	private String request;

	private Object resource;

	private int responseCode;

	private Map<String, String> requestHeaders = new HashMap<String, String>();

	private Map<String, String> responseHeaders = new HashMap<String, String>();

	private HTTPD(Socket sock)
	{
		this.sock = sock;
	}

	public static void main(String[] args) throws IOException
	{
		// Process arguments.
		if (args.length < 1 || args.length > 2) usage();
		rootDir = new File(args[0]);
		if (!rootDir.isDirectory()) usage();
		int port = 8080;
		if (args.length == 2)
		{
			try
			{
				port = Integer.parseInt(args[1]);
			}
			catch (NumberFormatException nfe)
			{
				usage();
			}
		}

		// Ready to go.
		ServerSocket serverSock = new ServerSocket(port);
		while (true)
		{
			Socket sock = serverSock.accept();
			// Paranoid security check since this is for local testing.
			if (!sock.getInetAddress().isLoopbackAddress())
			{
				System.out.println("Denying request from non-local IP "+sock.getInetAddress());
				sock.close();
				continue;
			}

			new Thread(new HTTPD(sock)).start();
		}
	}

	private static void usage()
	{
		System.err.println("Usage: java HTTPD <root-dir> [port]");
		System.err.println("Default port is 8080");
		System.exit(-1);
	}

	public void run()
	{
		try
		{
			final String utf8 = "UTF-8";
			BufferedOutputStream outbin = new BufferedOutputStream(sock.getOutputStream());
			PrintWriter out = new PrintWriter(new OutputStreamWriter(outbin, utf8));
			BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream(), utf8));

			InetAddress addr = sock.getInetAddress();
			String line;
			while ((line = in.readLine()) != null)
			{
				// First line
				if (request == null)
				{
					// Line should be of the format
					// METHOD resource HTTP/1.x
					String[] bits = line.split(" ");
					if (bits.length == 3)
					{
						if ("GET".equals(bits[0]))
						{
							request = bits[1];
						}
						else
						{
							// We don't support POST, HEAD, etc.
							responseCode = 501;
							resource = "Not supported";
						}
					}
					else
					{
						responseCode = 400;
						resource = "Bad request";
						break;
					}
				}
				// Blank line indicates end of headers
				else if (line.length() == 0)
				{
					break;
				}
				else
				{
					// Parse header.
					String[] bits = line.split(":");
					if (bits.length == 2)
					{
						requestHeaders.put(bits[0].trim().toLowerCase(), bits[1].trim().toLowerCase());
					}
					// else ignore
				}
			}

			// If we have a valid request, handle it.
			if (responseCode == 0)
			{
				handleRequest();
			}

			// Some handling is the same-ish for success or failure.
			int responseSize = 0;
			if (responseCode == 0 || resource == null)
			{
				responseCode = 501;
				resource = "Internal error";
			}
			if (resource instanceof String)
			{
				resource = resource.toString().getBytes(utf8);
				responseHeaders.put("Content-Encoding", utf8);
				responseHeaders.put("Content-Type", "text/plain; charset=UTF-8");
			}
			if (resource instanceof byte[]) responseSize = ((byte[])resource).length;
			else if (resource instanceof File) responseSize = (int)((File)resource).length();
			responseHeaders.put("Content-Length", Integer.toString(responseSize));

			// Reply.
			out.println("HTTP/1.1 "+responseCode);
			for (Map.Entry<String, String> e : responseHeaders.entrySet())
			{
				out.println(e.getKey()+": "+e.getValue());
			}
			out.println();
			// Ensure that we can write bytes to the binary stream ok.
			out.flush();
			if (resource instanceof byte[])
			{
				outbin.write((byte[])resource);
			}
			else if (resource instanceof File)
			{
				FileInputStream fis = new FileInputStream((File)resource);
				byte[] buf = new byte[4096];
				int len;
				while ((len = fis.read(buf)) != -1)
				{
					outbin.write(buf, 0, len);
				}
				fis.close();
			}

			// Minimal logging
			System.out.println("["+addr+"] "+responseCode+" ("+responseSize+" bytes) for "+request);

			outbin.close();
			in.close();
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}

	private void handleRequest()
	{
		// The request was correctly formatted. Decode it, perform security
		// checks, find the corresponding resource, and set up suitable response
		// headers.
		try
		{
			request = URLDecoder.decode(request, "UTF-8");
		}
		catch (UnsupportedEncodingException ufe)
		{
			// Failure to support UTF-8 is a problem with the Java environment.
			InternalError err = new InternalError();
			err.initCause(ufe);
			throw err;
		}

		// Super-paranoid check for attempts to access files outside the
		// permitted root directory.
		if (request.indexOf("..") >= 0)
		{
			responseCode = 401;
			resource = "Unauthorized";
			return;
		}

		// Basic processing.
		if (!request.startsWith("/"))
		{
			request = "/" + request;
		}
		File file = new File(rootDir, request);
		if (!file.isFile())
		{
			responseCode = 404;
			resource = "Not found";
		}
		else
		{
			responseCode = 200;
			resource = file;
			String contentType = getContentType(request);
			if (contentType != null)
			{
				responseHeaders.put("Content-Type", contentType);
			}
			else
			{
				responseHeaders.put("Content-Type", "application/x-unknown");
			}
		}

		// Extra hack for .pack.gz
		if (request.endsWith(".jar") &&
			requestHeaders.get("accept-encoding") != null &&
			requestHeaders.get("accept-encoding").contains("pack200-gzip"))
		{
			// We check for two possibilities.
			File _jar_pack_gz = new File(rootDir, request+".pack.gz");
			File _pack_gz = new File(rootDir, request.replace("jar$", "pack.gz"));
			if (_jar_pack_gz.isFile() || _pack_gz.isFile())
			{
				responseCode = 200;
				resource = _jar_pack_gz.isFile() ? _jar_pack_gz : _pack_gz;
				responseHeaders.put("Content-Type", "application/x-java-archive");
				responseHeaders.put("Content-Encoding", "pack200-gzip");
			}
		}
	}

	private String getContentType(String request)
	{
		// Note: we're not doing magic on the file or anything fancy
		if (request.endsWith(".html")) return "text/html";
		if (request.endsWith(".jar")) return "application/x-java-archive";
		return null;
	}
}

I got pack200 working under Tomcat6

  1. Install Tomcat
  2. If using Vista and Tomcat installed under Program Files, change permissions to allow modify & write
  3. Edit startup.bat in /bin to include “set JRE_HOME=C:\Program Files\Java\jre6” or whereever you have a JRE installed
  4. Check tomcat works when you run startup.bat, and stops with shutdown.bat
  5. Add directory ‘java’ under directory ‘webapps’.
  6. Create subdirectory ‘WEB-INF’ and copy in ‘web.xml’ from ‘webapps/ROOT/WEB-INF’
  7. Edit web.xml to include:
  1. Create directory ‘lib’ under ‘WEB-INF’
  2. Copy in ‘jnlp-servlet.jar’, which you will find in the sample/jnlp/servlet direvtory in the java sdk
  3. Add your .html and pack.gz files in the java directory. Note you also need the jar as the server looks for it - but it can be an empty file
  4. Point your browser at http://localhost:8080/java/yourhtmlfile.html

Sorry, just confirming, it is not possible to test a pack200 file locally? We have to do it via the tomcat method? Are people actually doing that, or just hoping it works ok as a pack200 file and uploading to the Java4K site blindly?

Look two posts before yours and you’ll find a small self-contained web server which supports pack200.

(I had posted it before, but I didn’t look in this thread. Oh well, never mind).

Hi pjt33. I can’t seem to get it working. I start it up: java HTTPD myDir

I have my html file with:


<applet CODE="A.class" ARCHIVE="MyApplet.pack.gz" WIDTH="1000" HEIGHT="500">
<param name="java_arguments" value="-Djnlp.packEnabled=true" />
</applet>

The web server reads the html file ok, but Java gives this error:


load: class A.class not found.
java.lang.ClassNotFoundException: A.class
	at sun.plugin2.applet.Applet2ClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.plugin2.applet.Plugin2ClassLoader.loadCode(Unknown Source)
	at sun.plugin2.applet.Plugin2Manager.createApplet(Unknown Source)
	at sun.plugin2.applet.Plugin2Manager$AppletExecutionRunnable.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Caused by: java.io.IOException: open HTTP connection failed:http://localhost:8080/A/class.class
	at sun.plugin2.applet.Applet2ClassLoader.getBytes(Unknown Source)
	at sun.plugin2.applet.Applet2ClassLoader.access$000(Unknown Source)
	at sun.plugin2.applet.Applet2ClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	... 7 more
Exception: java.lang.ClassNotFoundException: A.class

I’m running java 1.6.0_13.

The web server outputs this:


[/0:0:0:0:0:0:0:1] 200 (833 bytes) for /indexPack200.html
[/127.0.0.1] 404 (9 bytes) for /A.class
[/127.0.0.1] 404 (9 bytes) for /A/class.class

Any idea what I am doing wrong?

Thanks!

EDIT: Changed ARCHIVE=“MyApplet.pack.gz” to ARCHIVE=“MyApplet” and woohoo! It’s working! Thanks for the server code pjt33! :slight_smile: