/game_platform/lib/lwjgl-2.8.5/java/org/lwjgl/util/applet/AppletLoader.java
Java | 2269 lines | 1206 code | 404 blank | 659 comment | 263 complexity | f40c797d0634ce2f6a6a9c19101a9b11 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /*
- * Copyright (c) 2002-2008 LWJGL Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'LWJGL' nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- package org.lwjgl.util.applet;
- import java.applet.Applet;
- import java.applet.AppletStub;
- import java.awt.BorderLayout;
- import java.awt.Color;
- import java.awt.EventQueue;
- import java.awt.FontMetrics;
- import java.awt.Graphics;
- import java.awt.Image;
- import java.awt.MediaTracker;
- import java.awt.image.ImageObserver;
- import java.io.BufferedInputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.FilePermission;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.OutputStream;
- import java.io.PrintWriter;
- import java.io.StringWriter;
- import java.io.Writer;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.net.HttpURLConnection;
- import java.net.JarURLConnection;
- import java.net.SocketPermission;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.net.URLConnection;
- import java.security.AccessControlException;
- import java.security.AccessController;
- import java.security.AllPermission;
- import java.security.CodeSource;
- import java.security.PermissionCollection;
- import java.security.Permissions;
- import java.security.PrivilegedExceptionAction;
- import java.security.SecureClassLoader;
- import java.security.cert.Certificate;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.Queue;
- import java.util.StringTokenizer;
- import java.util.Vector;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.Future;
- import java.util.jar.JarEntry;
- import java.util.jar.JarFile;
- import java.util.jar.JarOutputStream;
- import java.util.jar.Pack200;
- import java.util.zip.CRC32;
- import java.util.zip.CheckedInputStream;
- import java.util.zip.GZIPInputStream;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipFile;
- /**
- * <p>
- * The AppletLoader enables deployment of LWJGL to applets in an easy
- * and polished way. The loader will display a configurable logo and progressbar
- * while the relevant jars (generic and native) are downloaded from a specified source.
- * </p>
- * <p>
- * The downloaded jars are extracted to the users temporary directory - and if enabled, cached for
- * faster loading in future uses.
- * </p>
- * <p>
- * The following applet parameters are required:
- * <ul>
- * <li>al_main - [String] Full package and class the applet to instantiate and display when loaded.</li>
- * <li>al_jars - [String] Comma separated list of jars to download.</li>
- * <p>
- * <li>al_windows - [String] Jar containing native files for windows.</li>
- * <li>al_linux - [String] Jar containing native files for linux.</li>
- * <li>al_mac - [String] Jar containing native files for mac.</li>
- * <li>al_solaris - [String] Jar containing native files for solaris.</li>
- * <li>al_freebsd - [String] Jar containing native files for freebsd.</li>
- * </ul>
- * </p>
- * <p>
- * Additionally the following parameters can be supplied to tweak the behaviour of the AppletLoader.
- * <ul>
- * <li>al_cache - [boolean] Whether to use cache system. <i>Default: true</i>.</li>
- * <li>al_version - [int or float] Version of deployment. If this is specified, the jars will be cached and
- * reused if the version matches. If version doesn't match all of the files are reloaded.</li>
- *
- * <li>al_debug - [boolean] Whether to enable debug mode. <i>Default: false</i>.</li>
- * <li>al_min_jre - [String] Specify the minimum jre version that the applet requires, should be in format like 1.6.0_24 or a subset like 1.6 <i>Default: 1.5</i>.</li>
- * <li>al_prepend_host - [boolean] Whether to limit caching to this domain, disable if your applet is hosted on multiple domains and needs to share the cache. <i>Default: true</i>.</li>
- * <li>al_lookup_threads - [int] Specify the number of concurrent threads to use to get file information before downloading. <i>Default: 1</i>.</li>
- * <p>
- * <li>al_windows64 - [String] If specified it will be used instead of al_windows on 64bit windows systems.</li>
- * <li>al_windows32 - [String] If specified it will be used instead of al_windows on 32bit windows systems.</li>
- * <li>al_linux64 - [String] If specified it will be used instead of al_linux on 64bit linux systems.</li>
- * <li>al_linux32 - [String] If specified it will be used instead of al_linux on 32bit linux systems.</li>
- * <li>al_mac32 - [String] If specified it will be used instead of al_mac on 64bit mac systems.</li>
- * <li>al_mac64 - [String] If specified it will be used instead of al_mac on 32bit mac systems.</li>
- * <li>al_macppc - [String] If specified it will be used instead of al_mac on PPC mac systems.</li>
- * <p>
- * <li>boxbgcolor - [String] any String AWT color ("red", "blue", etc), RGB (0-255) or hex formated color (#RRGGBB) to use as background. <i>Default: #ffffff</i>.</li>
- * <li>boxfgcolor - [String] any String AWT color ("red", "blue", etc), RGB (0-255) or hex formated color (#RRGGBB) to use as foreground. <i>Default: #000000</i>.</li>
- * <p>
- * <li>al_logo - [String Path of of the logo resource to paint while loading.<i>Default: "appletlogo.gif"</i>.</li>
- * <li>al_progressbar - [String] Path of the progressbar resource to paint on top of the logo, width clipped by percentage.<i>Default: "appletprogress.gif"</i>.</li>
- * <p>
- * <li>lwjgl_arguments - </li> [String] used to pass LWJGL parameters to LWJGL e.g. ("-Dorg.lwjgl.input.Mouse.allowNegativeMouseCoords=true -Dorg.lwjgl.util.Debug=true").</li>
- * </ul>
- * </p>
- * @author kappaOne <one.kappa@gmail.com>
- * @author Brian Matzon <brian@matzon.dk>
- * @version $Revision$
- * $Id$
- *
- * Contributors:
- * <ul>
- * <li>Arielsan</li>
- * <li>Bobjob</li>
- * <li>Dashiva</li>
- * <li>Dr_evil</li>
- * <li>Elias Naur</li>
- * <li>Kevin Glass</li>
- * <li>Matthias Mann</li>
- * <li>Mickelukas</li>
- * <li>NateS</li>
- * <li>Pelle Johnsen</li>
- * <li>Riven</li>
- * <li>Ruben01</li>
- * <li>Shannon Smith</li>
- * </ul>
- *
- */
- public class AppletLoader extends Applet implements Runnable, AppletStub {
- /** initializing */
- public static final int STATE_INIT = 1;
-
- /** checking version of jre */
- public static final int STATE_CHECK_JRE_VERSION = 2;
- /** determining which packages that are required */
- public static final int STATE_DETERMINING_PACKAGES = 3;
- /** checking for already downloaded files */
- public static final int STATE_CHECKING_CACHE = 4;
-
- /** checking if any updates are available for cache files */
- public static final int STATE_CHECKING_FOR_UPDATES = 5;
- /** downloading packages */
- public static final int STATE_DOWNLOADING = 6;
- /** extracting packages */
- public static final int STATE_EXTRACTING_PACKAGES = 7;
-
- /** validating packages */
- public static final int STATE_VALIDATING_PACKAGES = 8;
- /** updating the classpath */
- public static final int STATE_UPDATING_CLASSPATH = 9;
- /** switching to real applet */
- public static final int STATE_SWITCHING_APPLET = 10;
- /** initializing real applet */
- public static final int STATE_INITIALIZE_REAL_APPLET = 11;
- /** stating real applet */
- public static final int STATE_START_REAL_APPLET = 12;
- /** done */
- public static final int STATE_DONE = 13;
- /** used to calculate length of progress bar */
- protected volatile int percentage;
- /** total size of download in bytes */
- protected int totalDownloadSize;
- /** current size of extracted in bytes */
- protected int currentSizeExtract;
- /** total size of extracted in bytes */
- protected int totalSizeExtract;
- /** logo to be shown while loading */
- protected Image logo, logoBuffer;
- /** progressbar to render while loading */
- protected Image progressbar, progressbarBuffer;
- /** offscreen image used */
- protected Image offscreen;
- /** set to true while painting is done */
- protected boolean painting;
- /** background color of applet */
- protected Color bgColor = Color.white;
- /** color to write foreground in */
- protected Color fgColor = Color.black;
- /** urls of the jars to download */
- protected URL[] urlList;
- /** classLoader used to add downloaded jars to the classpath */
- protected ClassLoader classLoader;
- /** actual thread that does the loading */
- protected Thread loaderThread;
- /** animation thread that renders our load screen while loading */
- protected Thread animationThread;
- /** applet to load after all downloads are complete */
- protected Applet lwjglApplet;
- /** whether we're running in debug mode */
- protected boolean debugMode;
- /** whether to prepend host to cache path */
- protected boolean prependHost;
- /** Used to store file names with lastModified time */
- protected HashMap<String, Long> filesLastModified;
- /** Sizes of files to download */
- protected int[] fileSizes;
-
- /** Number of native jars */
- protected int nativeJarCount;
- /** whether to use caching system, only download files that have changed */
- protected boolean cacheEnabled;
- /** String to display as a subtask */
- protected String subtaskMessage = "";
- /** state of applet loader */
- protected volatile int state = STATE_INIT;
- /** whether lzma is supported */
- protected boolean lzmaSupported;
- /** whether pack200 is supported */
- protected boolean pack200Supported;
-
- /** whether to run in headless mode */
- protected boolean headless = false;
-
- /** whether to switch applets in headless mode or wait longer */
- protected boolean headlessWaiting = true;
- /** messages to be passed via liveconnect in headless mode */
- protected String[] headlessMessage;
-
- /** threads to use when fetching information of files to be downloaded */
- protected int concurrentLookupThreads;
-
- /** whether a fatal error occurred */
- protected boolean fatalError;
-
- /** whether a certificate refused error occurred */
- protected boolean certificateRefused;
-
- /** whether the minimum required JRE version is not found */
- protected boolean minimumJreNotFound;
- /** generic error message to display on error */
- protected String[] genericErrorMessage = { "An error occured while loading the applet.",
- "Please contact support to resolve this issue.",
- "<placeholder for error message>"};
- /** error message to display if user refuses to accept certificate*/
- protected String[] certificateRefusedMessage = { "Permissions for Applet Refused.",
- "Please accept the permissions dialog to allow",
- "the applet to continue the loading process."};
-
- /** error message to display if minimum JRE version is not met */
- protected String[] minimumJREMessage = { "Your version of Java is out of date.",
- "Visit java.com to get the latest version.",
- "Java <al_min_jre> or greater is required."};
-
- /** fatal error message to display */
- protected String[] errorMessage;
-
- /** have natives been loaded by another instance of this applet */
- protected static boolean natives_loaded;
-
- /*
- * @see java.applet.Applet#init()
- */
- public void init() {
- setState(STATE_INIT);
-
- // sanity check
- String[] requiredArgs = {"al_main", "al_jars"};
- for ( String requiredArg : requiredArgs ) {
- if ( getParameter(requiredArg) == null ) {
- fatalErrorOccured("missing required applet parameter: " + requiredArg, null);
- return;
- }
- }
-
- // whether to use cache system
- cacheEnabled = getBooleanParameter("al_cache", true);
- // whether to run in debug mode
- debugMode = getBooleanParameter("al_debug", false);
- // whether to prepend host to cache path
- prependHost = getBooleanParameter("al_prepend_host", true);
- // whether to run in headless mode
- headless = getBooleanParameter("al_headless", false);
-
- // obtain the number of concurrent lookup threads to use
- concurrentLookupThreads = getIntParameter("al_lookup_threads", 1); // defaults to 1
-
- // get colors of applet
- bgColor = getColor("boxbgcolor", Color.white);
- setBackground(bgColor);
- fgColor = getColor("boxfgcolor", Color.black);
- if (!headless) {
- // load logos
- logo = getImage(getStringParameter("al_logo", "appletlogo.gif"));
- progressbar = getImage(getStringParameter("al_progressbar", "appletprogress.gif"));
- }
- // check for lzma support
- try {
- Class.forName("LZMA.LzmaInputStream");
- lzmaSupported = true;
- } catch (Throwable e) {
- /* no lzma support */
- }
- // check pack200 support
- try {
- java.util.jar.Pack200.class.getSimpleName();
- pack200Supported = true;
- } catch (Throwable e) {
- /* no pack200 support */
- }
- }
- /**
- * Generates a stacktrace in the form of a string
- * @param exception Exception to make stacktrace of
- * @return Stacktrace of exception in the form of a string
- */
- private static String generateStacktrace(Exception exception) {
- Writer result = new StringWriter();
- PrintWriter printWriter = new PrintWriter(result);
- exception.printStackTrace(printWriter);
- return result.toString();
- }
- /*
- * @see java.applet.Applet#start()
- */
- public void start() {
- if (lwjglApplet != null) {
- lwjglApplet.start();
- }
- else {
- if(loaderThread == null && !fatalError) {
- loaderThread = new Thread(this);
- loaderThread.setName("AppletLoader.loaderThread");
- loaderThread.start();
- if (!headless) {
- animationThread = new Thread() {
- public void run() {
- while(loaderThread != null) {
- repaint();
- AppletLoader.this.sleep(100);
- }
- animationThread = null;
- }
- };
- animationThread.setName("AppletLoader.animationthread");
- animationThread.start();
- }
- }
- }
- }
- /*
- * @see java.applet.Applet#stop()
- */
- public void stop() {
- if (lwjglApplet != null) {
- lwjglApplet.stop();
- }
- }
- /*
- * @see java.applet.Applet#destroy()
- */
- public void destroy() {
- if (lwjglApplet != null) {
- lwjglApplet.destroy();
- }
- }
- /**
- * Clean up resources
- */
- protected void cleanUp() {
- progressbar = null;
- logo = null;
- logoBuffer = null;
- progressbarBuffer = null;
- offscreen = null;
- }
- /**
- * Retrieves the applet that has been loaded. Useful for liveconnect.
- */
- public Applet getApplet() {
- return lwjglApplet;
- }
- /**
- * Retrieves the current status of the AppletLoader and is
- * used by liveconnect when running in headless mode.
- *
- * This method will return the current progress of the AppletLoader
- * as a value from 0-100. In the case of a fatal error it will
- * return -1. If the certificate is refused it will return -2.
- * If the minimum jre requirement is not met will return -3.
- *
- * When method returns 100 the AppletLoader will sleep until the
- * method is called again. When called again it will switch to the
- * LWJGL Applet. This is a useful trigger to start the LWJGL applet
- * when needed.
- */
- public int getStatus() {
- if (fatalError) {
- headlessMessage = errorMessage;
-
- if (certificateRefused) return -2;
- if (minimumJreNotFound) return -3;
- return -1;
- }
-
- if (percentage == 100 && headlessWaiting) {
- headlessWaiting = false;
- }
-
- if (percentage == 95) {
- percentage = 100; // ready to switch applet
- }
-
- String[] message = {getDescriptionForState(), subtaskMessage};
- headlessMessage = message;
-
- return percentage;
- }
-
- /**
- * Retrieves the current message for the current status.
- * Used by liveconnect when running in headless mode.
- */
- public String[] getMessages() {
- return headlessMessage;
- }
-
- /**
- * Transfers the call of AppletResize from the stub to the lwjglApplet.
- */
- public void appletResize(int width, int height) {
- resize(width, height);
- }
- /*
- * @see java.awt.Container#update(java.awt.Graphics)
- */
- public final void update(Graphics g) {
- paint(g);
- }
- /*
- * @see java.awt.Container#paint(java.awt.Graphics)
- */
- public void paint(Graphics g) {
- // don't paint loader if applet loaded
- if(state == STATE_DONE) {
- cleanUp(); // clean up resources
- return;
- }
-
- // no drawing in headless mode
- if (headless) return;
- // create offscreen if missing
- if (offscreen == null) {
- offscreen = createImage(getWidth(), getHeight());
- // create buffers for animated gifs
- if (logo != null) {
- logoBuffer = createImage(logo.getWidth(null), logo.getHeight(null));
- // add image observer, it will notify when next animated gif frame is ready
- offscreen.getGraphics().drawImage(logo, 0, 0, this);
- // in case image is not animated fill image buffer once
- imageUpdate(logo, ImageObserver.FRAMEBITS, 0, 0, 0, 0);
- }
- if (progressbar != null) {
- progressbarBuffer = createImage(progressbar.getWidth(null), progressbar.getHeight(null));
- // add image observer, it will notify when next animated gif frame is ready
- offscreen.getGraphics().drawImage(progressbar, 0, 0, this);
- // in case image is not animated fill image buffer once
- imageUpdate(progressbar, ImageObserver.FRAMEBITS, 0, 0, 0, 0);
- }
- }
- // draw everything onto an image before drawing to avoid flicker
- Graphics og = offscreen.getGraphics();
- FontMetrics fm = og.getFontMetrics();
- // clear background color
- og.setColor(bgColor);
- og.fillRect(0, 0, offscreen.getWidth(null), offscreen.getHeight(null));
- og.setColor(fgColor);
-
- // if we had a failure of some sort, notify the user
- if (fatalError) {
- for(int i=0; i<errorMessage.length; i++) {
- if(errorMessage[i] != null) {
- int messageX = (offscreen.getWidth(null) - fm.stringWidth(errorMessage[i])) / 2;
- int messageY = (offscreen.getHeight(null) - (fm.getHeight() * errorMessage.length)) / 2;
- og.drawString(errorMessage[i], messageX, messageY + i*fm.getHeight());
- }
- }
- } else {
- og.setColor(fgColor);
- painting = true;
- // get position at the middle of the offscreen buffer
- int x = offscreen.getWidth(null)/2;
- int y = offscreen.getHeight(null)/2;
- // draw logo
- if (logo != null) {
- og.drawImage(logoBuffer, x-logo.getWidth(null)/2, y-logo.getHeight(null)/2, this);
- }
- // draw message
- String message = getDescriptionForState();
-
- int messageX = (offscreen.getWidth(null) - fm.stringWidth(message)) / 2;
- int messageY = y + 20;
- if (logo != null) messageY += logo.getHeight(null)/2;
- else if (progressbar != null) messageY += progressbar.getHeight(null)/2;
- og.drawString(message, messageX, messageY);
- // draw subtaskmessage, if any
- if(subtaskMessage.length() > 0) {
- messageX = (offscreen.getWidth(null) - fm.stringWidth(subtaskMessage)) / 2;
- og.drawString(subtaskMessage, messageX, messageY+20);
- }
- // draw loading progress bar, clipping it depending on percentage done
- if (progressbar != null) {
- int barSize = (progressbar.getWidth(null) * percentage) / 100;
- og.clipRect(x-progressbar.getWidth(null)/2, 0, barSize, offscreen.getHeight(null));
- og.drawImage(progressbarBuffer, x-progressbar.getWidth(null)/2, y-progressbar.getHeight(null)/2, this);
- }
- painting = false;
- }
- og.dispose();
- // finally draw it all centred
- g.drawImage(offscreen, (getWidth() - offscreen.getWidth(null))/2, (getHeight() - offscreen.getHeight(null))/2, null);
- }
- /**
- * When an animated gif frame is ready to be drawn the ImageObserver
- * will call this method.
- *
- * The Image frame is copied into a buffer, which is then drawn.
- * This is done to prevent image tearing on gif animations.
- */
- public boolean imageUpdate(Image img, int flag, int x, int y, int width, int height) {
- // finish with this ImageObserver
- if (state == STATE_DONE) return false;
- // if image frame is ready to be drawn and is currently not being painted
- if (flag == ImageObserver.FRAMEBITS && !painting) {
- Image buffer;
- // select which buffer to fill
- if (img == logo) buffer = logoBuffer;
- else buffer = progressbarBuffer;
- Graphics g = buffer.getGraphics();
- // clear background on buffer
- g.setColor(bgColor);
- g.fillRect(0, 0, buffer.getWidth(null), buffer.getHeight(null));
- // buffer background is cleared, so draw logo under progressbar
- if (img == progressbar && logo != null) {
- g.drawImage(logoBuffer, progressbar.getWidth(null)/2-logo.getWidth(null)/2,
- progressbar.getHeight(null)/2-logo.getHeight(null)/2, null);
- }
- g.drawImage(img, 0, 0, this);
- g.dispose();
- repaint();
- }
- return true;
- }
- /**
- * @return string describing the state of the loader
- */
- protected String getDescriptionForState() {
- switch (state) {
- case STATE_INIT:
- return "Initializing loader";
- case STATE_CHECK_JRE_VERSION:
- return "Checking version";
- case STATE_DETERMINING_PACKAGES:
- return "Determining packages to load";
- case STATE_CHECKING_CACHE:
- return "Calculating download size";
- case STATE_CHECKING_FOR_UPDATES:
- return "Checking for updates";
- case STATE_DOWNLOADING:
- return "Downloading packages";
- case STATE_EXTRACTING_PACKAGES:
- return "Extracting downloaded packages";
- case STATE_VALIDATING_PACKAGES:
- return "Validating packages";
- case STATE_UPDATING_CLASSPATH:
- return "Updating classpath";
- case STATE_SWITCHING_APPLET:
- return "Switching applet";
- case STATE_INITIALIZE_REAL_APPLET:
- return "Initializing real applet";
- case STATE_START_REAL_APPLET:
- return "Starting real applet";
- case STATE_DONE:
- return "Done loading";
- default:
- return "unknown state";
- }
- }
- /**
- * Trims the passed file string based on the available capabilities
- * @param file string of files to be trimmed
- * @return trimmed string based on capabilities of client
- */
- protected String trimExtensionByCapabilities(String file) {
- if (!pack200Supported) {
- file = file.replace(".pack", "");
- }
- if (!lzmaSupported && file.endsWith(".lzma")) {
- file = file.replace(".lzma", "");
- System.out.println("LZMA decoder (lzma.jar) not found, trying " + file + " without lzma extension.");
- }
- return file;
- }
- /**
- * Reads list of jars to download and adds the urls to urlList
- * also finds out which OS you are on and adds appropriate native
- * jar to the urlList
- */
- protected void loadJarURLs() throws Exception {
- setState(STATE_DETERMINING_PACKAGES);
- // jars to load
- String jarList = getParameter("al_jars");
- String nativeJarList = null;
-
- String osName = System.getProperty("os.name");
-
- if (osName.startsWith("Win")) {
- // check if arch specific natives have been specified
- if (System.getProperty("os.arch").endsWith("64")) {
- nativeJarList = getParameter("al_windows64");
- } else {
- nativeJarList = getParameter("al_windows32");
- }
- if (nativeJarList == null) {
- nativeJarList = getParameter("al_windows");
- }
- } else if (osName.startsWith("Linux") || osName.startsWith("Unix")) {
- // check if arch specific natives have been specified
- if (System.getProperty("os.arch").endsWith("64")) {
- nativeJarList = getParameter("al_linux64");
- } else {
- nativeJarList = getParameter("al_linux32");
- }
- if (nativeJarList == null) {
- nativeJarList = getParameter("al_linux");
- }
- } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
-
- // check if arch specific natives have been specified
- if (System.getProperty("os.arch").endsWith("64")) {
- nativeJarList = getParameter("al_mac64");
- } else if (System.getProperty("os.arch").contains("ppc")) {
- nativeJarList = getParameter("al_macppc");
- } else {
- nativeJarList = getParameter("al_mac32");
- }
- if (nativeJarList == null) {
- nativeJarList = getParameter("al_mac");
- }
-
- } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) {
- nativeJarList = getParameter("al_solaris");
- } else if (osName.startsWith("FreeBSD")) {
- nativeJarList = getParameter("al_freebsd");
- } else {
- fatalErrorOccured("OS (" + osName + ") not supported", null);
- return;
- }
-
- if (nativeJarList == null) {
- fatalErrorOccured("no lwjgl natives files found", null);
- return;
- }
-
- jarList = trimExtensionByCapabilities(jarList);
- StringTokenizer jars = new StringTokenizer(jarList, ", ");
- nativeJarList = trimExtensionByCapabilities(nativeJarList);
- StringTokenizer nativeJars = new StringTokenizer(nativeJarList, ", ");
-
- int jarCount = jars.countTokens();
- nativeJarCount = nativeJars.countTokens();
-
- urlList = new URL[jarCount+nativeJarCount];
- URL path = getCodeBase();
- // set jars urls
- for (int i = 0; i < jarCount; i++) {
- urlList[i] = new URL(path, jars.nextToken());
- }
- for (int i = jarCount; i < jarCount+nativeJarCount; i++) {
- urlList[i] = new URL(path, nativeJars.nextToken());
- }
- }
- /**
- * 9 steps
- *
- * 1) check jre version meets minimum requirements
- * 2) check applet cache and decide which jars to download
- * 3) download the jars
- * 4) extract native files
- * 5) validate jars for any corruption
- * 6) save applet cache information
- * 7) add jars to class path
- * 8) set any lwjgl properties
- * 9) switch to loaded applet
- */
- public void run() {
- percentage = 5;
- try {
- debug_sleep(2000);
-
- // check JRE version meets minimum requirements
- if (!isMinJREVersionAvailable()) {
- minimumJreNotFound = true;
- fatalErrorOccured("Java " + getStringParameter("al_min_jre", "1.5") + " or greater is required.", null);
- return;
- }
- // parse the urls for the jars into the url list
- loadJarURLs();
- // get path where applet files will be stored
- String path = getCacheDirectory();
- File dir = new File(path);
- // create directory
- if (!dir.exists()) {
- dir.mkdirs();
- }
- File versionFile = new File(dir, "version");
- // if specified applet version already available don't download anything
- boolean versionAvailable = false;
- // version string of applet
- String version = getParameter("al_version");
- // if applet version specifed, compare with version in the cache
- if (version != null) {
- versionAvailable = compareVersion(versionFile, version.toLowerCase());
- }
- // if jars not available or need updating download them
- if (!versionAvailable) {
- // get jars file sizes and check cache
- getJarInfo(dir); // 5-15%
- // downloads jars from the server
- downloadJars(path); // 15-55%
- // Extract Pack and LZMA files
- extractJars(path); // 55-65%
- // Extracts Native Files
- extractNatives(path); // 65-80%
-
- // Validate Jars // 80-90%
- validateJars(path);
- // save version information once jars downloaded successfully
- if (version != null) {
- percentage = 90;
- writeObjectFile(versionFile, version.toLowerCase());
- }
- // save file names with last modified info once downloaded successfully
- writeObjectFile(new File(dir, "timestamps"), filesLastModified);
- }
- // add the downloaded jars and natives to classpath
- updateClassPath(path);
-
- // set lwjgl properties
- setLWJGLProperties();
- // if headless mode then sleep, until told to continue
- if (headless) {
- while(headlessWaiting) {
- Thread.sleep(100);
- }
- }
-
- // make applet switch on the EDT as an AWT/Swing permission dialog could be called
- EventQueue.invokeAndWait(new Runnable() {
- public void run() {
- try {
- switchApplet();
- } catch (Exception e) {
- fatalErrorOccured("This occurred while '" + getDescriptionForState() + "'", e);
- }
- setState(STATE_DONE);
- repaint();
- }
- });
-
- } catch (Exception e) {
- certificateRefused = e instanceof AccessControlException;
- fatalErrorOccured("This occurred while '" + getDescriptionForState() + "'", e);
- } finally {
- loaderThread = null;
- }
- }
-
- /**
- * When this method is supplied with a JRE version it will compare it to the
- * current JRE version.
- *
- * minimum requried JRE version is set using al_min_jre parameter, if not
- * this is not set then the value will default to version 1.5
- *
- * The minimumVersion should follow a structure such as x.x.x_x
- * Example values would include 1.6.0_10 or a subset like 1.6.0 or 1.6
- *
- * @return returns true if the available version is greater or equal to the
- * minimum version required
- *
- * @throws Exception a NumberFormatException is thrown if the string is not valid
- */
- public boolean isMinJREVersionAvailable() throws Exception {
- setState(STATE_CHECK_JRE_VERSION);
-
- String minimumVersion = getStringParameter("al_min_jre", "1.5");
- String javaVersion = System.getProperty("java.version");
-
- // remove dash and anything after it (letters) from version string e.g. 1.5.0_01-ea
- minimumVersion = javaVersion.split("-")[0];
- javaVersion = minimumVersion.split("-")[0];
-
- // split version string into a string arrays
- String[] jvmVersionData = javaVersion.split("[_\\.]");
- String[] minVersionData = minimumVersion.split("[_\\.]");
-
- int maxLength = Math.max(jvmVersionData.length, minVersionData.length);
-
- // convert string arrays into int arrays
- int[] jvmVersion = new int[maxLength];
- int[] minVersion = new int[maxLength];
-
- for (int i = 0; i < jvmVersionData.length; i++) {
- jvmVersion[i] = Integer.parseInt(jvmVersionData[i]);
- }
-
- for (int i = 0; i < minVersionData.length; i++) {
- minVersion[i] = Integer.parseInt(minVersionData[i]);
- }
-
- // compare versions
- for (int i = 0; i < maxLength; i++) {
- if (jvmVersion[i] < minVersion[i]) return false; // minVersion is greater then jvmVersion
- }
-
- return true;
- }
-
- /**
- * This method will return true if the version stored in the file
- * matches the supplied String version.
- *
- * @param versionFile - location to file containing version information
- * @param version - String version that needs to be compared
- * @return returns true if the version in file matches specified version
- */
- protected boolean compareVersion(File versionFile, String version) {
- // if version file exists
- if (versionFile.exists()) {
- String s = readStringFile(versionFile);
-
- // compare to version with file
- if (s != null && s.equals(version)) {
- percentage = 90; // not need to download cache files again
- if(debugMode) {
- System.out.println("Loading Cached Applet Version: " + version);
- }
- debug_sleep(2000);
-
- return true; // version matches file
- }
- }
-
- return false;
- }
-
- /**
- * Parses the java_arguments list and sets lwjgl specific
- * properties accordingly, before the launch.
- */
- protected void setLWJGLProperties() {
- String lwjglArguments = getParameter("lwjgl_arguments");
- if(lwjglArguments != null && lwjglArguments.length() > 0) {
- int start = lwjglArguments.indexOf("-Dorg.lwjgl");
- while(start != -1) {
- int end = lwjglArguments.indexOf(" ", start);
- if(end == -1) {
- end = lwjglArguments.length();
- }
- String[] keyValue = lwjglArguments.substring(start+2, end).split("=");
- System.setProperty(keyValue[0], keyValue[1]);
- if(debugMode) {
- System.out.println("Setting property " + keyValue[0] + " to " + keyValue[1]);
- }
- start = lwjglArguments.indexOf("-Dorg.lwjgl", end);
- }
- }
- }
- /**
- * This method will return the location of the cache directory. All the
- * applet files will be downloaded and stored here. A folder will be
- * created inside the LWJGL cache directory from the al_title parameter.
- * This folder will also be prepended by the host name of the codebase
- * to avoid conflict with same named applets on other hosts.
- *
- * @return path to applets cache directory
- * @throws Exception if access is denied
- */
- protected String getCacheDirectory() throws Exception {
-
- String path = AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
- public String run() throws Exception {
- // we append the code base to avoid naming collisions with al_title
- String codebase = "";
- if(prependHost) {
- codebase = getCodeBase().getHost();
- if(codebase == null || codebase.length() == 0) {
- codebase = "localhost";
- }
- codebase += File.separator;
- }
- return getLWJGLCacheDir() + File.separator + codebase + getParameter("al_title") + File.separator;
- }
- });
-
- return path;
- }
-
- /**
- * Get path to the lwjgl cache directory. This location will be where
- * the OS keeps temporary files.
- *
- * @return path to the lwjgl cache directory
- */
- protected String getLWJGLCacheDir() {
- String cacheDir = System.getProperty("deployment.user.cachedir");
-
- if (cacheDir == null || System.getProperty("os.name").startsWith("Win")) {
- cacheDir = System.getProperty("java.io.tmpdir");
- }
-
- return cacheDir + File.separator + "lwjglcache";
- }
- /**
- * read String object from File
- *
- * @param file to be read
- * @return the String stored in the file or null if it fails
- */
- protected String readStringFile(File file) {
- try {
- return (String)readObjectFile(file);
- } catch (Exception e) {
- // failed to read version file
- e.printStackTrace();
- }
-
- // return null if failed to read file
- return null;
- }
-
- /**
- * read the HashMap from File
- *
- * @param file the file to read
- * @return the hashmap stored in the file or an empty hashmap if it fails
- */
- @SuppressWarnings("unchecked")
- protected HashMap<String, Long> readHashMapFile(File file) {
-
- try {
- return (HashMap<String, Long>) readObjectFile(file);
- } catch (Exception e) {
- // failed to read hashmap from file
- e.printStackTrace();
- }
-
- // return an empty map if failed to read file
- return new HashMap<String, Long>();
- }
-
- /**
- * read the object from the File
- *
- * @param file the file to read
- * @return the object contained in the file or null if it fails
- * @throws Exception if it fails to read object from file
- */
- protected Object readObjectFile(File file) throws Exception {
- FileInputStream fis = new FileInputStream(file);
-
- try {
- ObjectInputStream dis = new ObjectInputStream(fis);
- Object object = dis.readObject();
- dis.close();
- return object;
- } catch (Exception e) {
- // failed to read file
- throw e;
- } finally {
- fis.close();
- }
- }
-
- /**
- * write object to specified File
- *
- * @param file the file to write out to
- * @param object the contents of the file
- * @throws Exception if it fails to write file
- */
- protected void writeObjectFile(File file, Object object) throws Exception {
- FileOutputStream fos = new FileOutputStream(file);
- try {
- ObjectOutputStream dos = new ObjectOutputStream(fos);
- dos.writeObject(object);
- dos.close();
- } finally {
- fos.close();
- }
- }
- /**
- * Edits the ClassPath at runtime to include the jars
- * that have just been downloaded and then adds the
- * lwjgl natives folder property.
- *
- * @param path location where applet is stored
- * @throws Exception if it fails to add classpath
- */
- protected void updateClassPath(final String path) throws Exception {
- setState(STATE_UPDATING_CLASSPATH);
- percentage = 95;
- URL[] urls = new URL[urlList.length];
- for (int i = 0; i < urlList.length; i++) {
- String file = new File(path, getJarName(urlList[i])).toURI().toString();
- // fix JVM bug where ! is not escaped
- file = file.replace("!", "%21");
- urls[i] = new URL(file);
- }
-
- // get AppletLoader certificates
- final Certificate[] certs = getCurrentCertificates();
-
- // detect if we are running on a mac and save result as boolean
- String osName = System.getProperty("os.name");
- final boolean isMacOS = (osName.startsWith("Mac") || osName.startsWith("Darwin"));
-
- // add downloaded jars to the classpath with required permissions
- classLoader = new URLClassLoader(urls) {
- protected PermissionCollection getPermissions (CodeSource codesource) {
- PermissionCollection perms = null;
- try {
-
- // if mac, apply workaround for the multiple security dialog issue
- if (isMacOS) {
- // if certificates match the AppletLoader certificates then don't use SecureClassLoader to get further permissions
- if (certificatesMatch(certs, codesource.getCertificates())) {
- perms = new Permissions();
- perms.add(new AllPermission());
- return perms;
- }
- }
- // getPermissions from original classloader is important as it checks for signed jars and shows any security dialogs needed
- Method method = SecureClassLoader.class.getDeclaredMethod("getPermissions", new Class[] { CodeSource.class });
- method.setAccessible(true);
- perms = (PermissionCollection)method.invoke(getClass().getClassLoader(), new Object[] {codesource});
- String host = getCodeBase().getHost();
- if (host != null && (host.length() > 0)) {
- // add permission for downloaded jars to access host they were from
- perms.add(new SocketPermission(host, "connect,accept"));
- }
- else if ( "file".equals(codesource.getLocation().getProtocol()) ) {
- // if running locally add file permission
- String path = codesource.getLocation().getFile().replace('/', File.separatorChar);
- perms.add(new FilePermission(path, "read"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return perms;
- }
-
- // allow non lwjgl native to be found from cache directory
- protected String findLibrary (String libname) {
- String libPath = path + "natives" + File.separator + System.mapLibraryName(libname);
-
- if (new File(libPath).exists()) {
- return libPath;
- }
-
- return super.findLibrary(libname);
- }
- };
- debug_sleep(2000);
- // unload natives loaded by a previous instance of this lwjgl applet
- unloadNatives(path);
- // add natives files path to native class path
- System.setProperty("org.lwjgl.librarypath", path + "natives");
- // Make sure jinput knows about the new path too
- System.setProperty("net.java.games.input.librarypath", path + "natives");
- // set the library path, useful for non lwjgl natives
- System.setProperty("java.library.path", path + "natives");
- // mark natives as loaded
- natives_loaded = true;
- }
- /**
- * Unload natives loaded by a different classloader.
- *
- * Due to limitations of the jvm, native files can only
- * be loaded once and only be used by the classloader
- * they were loaded from.
- *
- * Due to the way applets on plugin1 work, one jvm must
- * be used for all applets. We need to use multiple
- * classloaders in the same jvm due to LWJGL's static
- * nature. In order to solve this we simply remove the
- * natives from a previous classloader allowing a new
- * classloader to use those natives in the same jvm.
- *
- * This method will only attempt to unload natives from a
- * previous classloader if it detects that the natives have
- * been loaded in the same jvm.
- *
- * @param nativePath directory where natives are stored
- */
- private void unloadNatives(String nativePath) {
- // check whether natives have been loaded into this jvm
- if (!natives_loaded) {
- return;
- }
- try {
- Field field = ClassLoader.class.getDeclaredField("loadedLibraryNames");
- field.setAccessible(true);
- Vector libs = (Vector) field.get(getClass().getClassLoader());
- String path = new File(nativePath).getCanonicalPath();
- for (int i = 0; i < libs.size(); i++) {
- String s = (String) libs.get(i);
- // if a native from the nativePath directory is loaded, unload it
- if (s.startsWith(path)) {
- libs.remove(i);
- i--;
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * replace the current applet with the lwjgl applet
- * using AppletStub and initialise and start it
- */
- protected void switchApplet() throws Exception {
- setState(STATE_SWITCHING_APPLET);
- percentage = 100;
- debug_sleep(2000);
-
- // set correct context classloader for lwjgl applet
- Thread.currentThread().setContextClassLoader(classLoader);
-
- Class appletClass = classLoader.loadClass(getParameter("al_main"));
- lwjglApplet = (Applet) appletClass.newInstance();
- lwjglApplet.setStub(this);
- lwjglApplet.setSize(getWidth(), getHeight());
- setLayout(new BorderLayout());
- add(lwjglApplet);
- validate();
- setState(STATE_INITIALIZE_REAL_APPLET);
- lwjglApplet.init();
- setState(STATE_START_REAL_APPLET);
- lwjglApplet.start();
- }
- /**
- * This method will get the files sizes of the files to download.
- * It wil further get the lastModified time of files
- * and save it in a hashmap, if cache is enabled it will mark
- * those files that have not changed since last download to not
- * redownloaded.
- *
- * @param dir - location to read cache file from
- * @throws Exception - if fails to get infomation
- */
- protected void getJarInfo(File dir) throws Exception {
- setState(STATE_CHECKING_CACHE);
-
- filesLastModified = new HashMap<String, Long>();
- // store file sizes and mark which files not to download
- fileSizes = new int[urlList.length];
- File timestampsFile = new File(dir, "timestamps");
- // if timestamps file exists, load it
- if (timestampsFile.exists()) {
- setState(STATE_CHECKING_FOR_UPDATES);
- filesLastModified = readHashMapFile(timestampsFile);
- }
- // calculate total size of jars to download
-
- ExecutorService executorService = Executors.newFixedThreadPool(concurrentLookupThreads);
- Queue<Future> requests = new LinkedList<Future>();
-
- // create unique object to sync code in requests
- final Object sync = new Integer(1);
-
- for (int j = 0; j < urlList.length; j++) {
- final int i = j;
-
- Future request = executorService.submit(new Runnable() {
-
- public void run() {
-
- try {
-
- URLConnection urlconnection = urlList[i].openConnection();
- urlconnection.setDefaultUseCaches(false);
- if (urlconnection instanceof HttpURLConnection) {
- ((HttpURLConnection) urlconnection).setRequestMethod("HEAD");
- }
-
- fileSizes[i] = urlconnection.getContentLength();
- long lastModified = urlconnection.getLastModified();
- String fileName = getFileName(urlList[i]);
- if (cacheEnabled && lastModified != 0 && filesLastModified.containsKey(fileName)) {
- long savedLastModified = filesLastModified.get(fileName);
- // if lastModifed time is the same, don't redownload
- if (savedLastModified == lastModified) {
- fileSizes[i] = -2; // mark it to not redownload
- }
- }
-
- if (fileSizes[i] >= 0) {
- synchronized (sync) {
- totalDownloadSize += fileSizes[i];
- }
- }
- // put key and value in the hashmap
- filesLastModified.put(fileName, lastModified);
-
- } catch (Exception e) {
- throw new RuntimeException("Failed to fetch information for " + urlList[i], e);
- }
- }});
-
- requests.add(request);
- }
-
- while (!requests.isEmpty()) {
- Iterator<Future> iterator = requests.iterator();
- while (iterator.hasNext()) {
- Future request = iterator.next();
- if (request.isDone()) {
- request.get(); // will throw an exception if request thrown an exception.
- iterator.remove();
-
- // update progress bar
- percentage = 5 + (int) (10 * (urlList.length - requests.size()) / (float) urlList.length);
- }
- }
-
- Thread.sleep(10);
- }
-
- executorService.shutdown();
- }
- /**
- * Will download the jars from the server using the list of urls
- * in urlList, while at the same time updating progress bar
- *
- * @param path location of the directory to save to
- * @throws Exception if download fails
- */
- protected void downloadJars(String path) throws Exception {
- setState(STATE_DOWNLOADING);
- URLConnection urlconnection;
- int initialPercentage = percentage = 15;
- int amountDownloaded = 0;
- // download each jar
- byte buffer[] = new byte[65536];
- for (int i = 0; i < urlList.length; i++) {
- // skip file if marked as -2 (already downloaded and not changed)
- if (fileSizes[i] == -2) continue;
- int unsuccessfulAttempts = 0;
- int maxUnsuccessfulAttempts = 3;
- boolean downloadFile = true;
-
- String currentFile = getFileName(urlList[i]);
- // download the jar a max of 3 times
- while(downloadFile) {
- downloadFile = false;
- debug_sleep(2000);
-
- try {
- urlconnection = urlList[i].openConnection();
- urlconnection.setUseCaches(false);
-
- if (urlconnection instanceof HttpURLConnection) {
- urlconnection.setRequestProperty("Cache-Control", "no-store,max-age=0,no-cache");
- urlconnection.connect();
- }
-
-
- InputStream inputstream = getJarInputStream(currentFile, urlconnection);
- FileOutputStream fos = new FileOutputStream(path + currentFile);
-
-
- int bufferSize;
- int currentDownload = 0;
-
- long downloadStartTime = System.currentTimeMillis();
- int downloadedAmount = 0;
- String downloadSpeedMessage = "";
-
- try {
- while ((bufferSize = inputstream.read(buffer, 0, buffer.length)) != -1) {
- debug_sleep(10);
- fos.write(buffer, 0, bufferSize);
- currentDownload += bufferSize;
-
- int totalDownloaded = amountDownloaded + currentDownload;
- percentage = initialPercentage + ((totalDownloaded * 45) / totalDownloadSize);
- subtaskMessage = "Retrieving: " + currentFile + " " + ((totalDownloaded * 100) / totalDownloadSize) + "%";
-
- downloadedAmount += bufferSize;
- long timeLapse = System.currentTimeMillis() - downloadStartTime;
- // update only if a second or more has passed
- if (timeLapse >= 1000) {
- // get kb/s, nice that bytes/millis is same as kilobytes/seconds
- float downloadSpeed = (float) downloadedAmount / timeLapse;
- // round to two decimal places
- downloadSpeed = ((int)(downloadSpeed*100))/100f;
- // set current speed message
- downloadSpeedMessage = " - " + downloadSpeed + " KB/sec";
- // reset downloaded amount
- downloadedAmount = 0;
- // reset start time
- downloadStartTime = System.currentTimeMillis();
- }
-
- subtaskMessage += downloadSpeedMessage;
- }
-
- } finally {
- inputstream.close();
- fos.close();
- }
-
- // download complete, verify if it was successful
- if (urlconnection instanceof HttpURLConnection) {
- if (currentDownload == fileSizes[i]) {
- // successful download
- }
- else if (fileSizes[i] <= 0 && currentDownload != 0) {
- // If contentLength for fileSizes[i] <= 0, we don't know if the download
- // is complete. We're going to guess the download is complete.
- }
- else {
- throw new Exception("size mismatch on download of " + currentFile +
- " expected " + fileSizes[i] + " got " + currentDownload);
- }
- }
-
- // successful file download, update total amount downloaded
- amountDownloaded += fileSizes[i];
-
- } catch (Exception e) {
- e.printStackTrace(); // output exception to console
-
- // Failed to download the file
- unsuccessfulAttempts++;
-
- // download failed try again
- if (unsuccessfulAttempts < maxUnsuccessfulAttempts) {
- downloadFile = true;
- Thread.sleep(100); // wait a bit before retrying
- }
- else {
- // retry attempts exhasted, download failed
- throw new Exception("failed to download " + currentFile +
- " after " + maxUnsuccessfulAttempts + " attempts");
- }
- }
- }
- }
- subtaskMessage = "";
- }
- /**
- * Retrieves a jar files input stream. This method exists primarily to fix an Opera hang in getInputStream
- * @param urlconnection connection to get input stream from
- * @return InputStream or null if not possible
- */
- protected InputStream getJarInputStream(final String currentFile, final URLConnection urlconnection) throws Exception {
- final InputStream[] is = new InputStream[1];
- // try to get the input stream 3 times.
- // Wait at most 5 seconds before interrupting the thread
- for (int j = 0; j < 3 && is[0] == null; j++) {
- Thread t = new Thread() {
- public void run() {
- try {
- is[0] = urlconnection.getInputStream();
- } catch (IOException e) {
- /* ignored */
- }
- }
- };
- t.setName("JarInputStreamThread");
- t.start();
- int iterationCount = 0;
- while(is[0] == null && iterationCount++ < 5) {
- try {
- t.join(1000);
- } catch (InterruptedE…
Large files files are truncated, but you can click here to view the full file