From: <ma...@us...> - 2007-06-10 20:30:06
|
Revision: 2848 http://svn.sourceforge.net/java-game-lib/?rev=2848&view=rev Author: matzon Date: 2007-06-10 13:30:03 -0700 (Sun, 10 Jun 2007) Log Message: ----------- initial checking on AppletLoader Added Paths: ----------- trunk/LWJGL/res/appletlogo.png trunk/LWJGL/res/appletprogress.gif trunk/LWJGL/src/java/org/lwjgl/test/applet/AppletLoaderTest.java trunk/LWJGL/src/java/org/lwjgl/util/applet/AppletLoader.java Added: trunk/LWJGL/res/appletlogo.png =================================================================== (Binary files differ) Property changes on: trunk/LWJGL/res/appletlogo.png ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/LWJGL/res/appletprogress.gif =================================================================== (Binary files differ) Property changes on: trunk/LWJGL/res/appletprogress.gif ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/LWJGL/src/java/org/lwjgl/test/applet/AppletLoaderTest.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/applet/AppletLoaderTest.java (rev 0) +++ trunk/LWJGL/src/java/org/lwjgl/test/applet/AppletLoaderTest.java 2007-06-10 20:30:03 UTC (rev 2848) @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006 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.test.applet; + +import java.applet.Applet; +import java.awt.BorderLayout; +import java.awt.Canvas; + +import org.lwjgl.opengl.AWTInputAdapter; + +public class AppletLoaderTest extends Applet { + + Test test = null; + + public void destroy() { + super.destroy(); + System.out.println("*** destroy ***"); + AWTInputAdapter.destroy(); + } + + public void start() { + super.start(); + System.out.println("*** start ***"); + } + + public void stop() { + super.stop(); + System.out.println("*** stop ***"); + test.stop(); + } + + public void init() { + System.out.println("*** init ***"); + + setLayout(new BorderLayout()); + try { + test = (Test) Class.forName(getParameter("test")).newInstance(); + Canvas canvas = (Canvas) test; + canvas.setSize(getWidth(), getHeight()); + add(canvas); + } catch (Exception e) { + e.printStackTrace(); + } + test.start(); + } +} Added: trunk/LWJGL/src/java/org/lwjgl/util/applet/AppletLoader.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/applet/AppletLoader.java (rev 0) +++ trunk/LWJGL/src/java/org/lwjgl/util/applet/AppletLoader.java 2007-06-10 20:30:03 UTC (rev 2848) @@ -0,0 +1,907 @@ +/* + * Copyright (c) 2002-2007 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.Color; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.Toolkit; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.security.cert.Certificate; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * <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 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_logo - [String Path of of the logo resource to paint while loading.</li> + * <li>al_progressbar - [String] Path of the progressbar resource to paint on top of the logo, width clipped by percentage.</li> + * <li>al_jars - [String] Comma seperated list of jars to download.</li> + * <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> + * </ul> + * </p> + * <p> + * Additionally the following parameters can be supplied to tweak the behaviour of the AppletLoader. + * <ul> + * <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_bgcolor - [String] Hex formated color to use as background. <i>Default: ffffff</i>.</li> + * <li>al_fgcolor - [String] Hex formated color to use as foreground. <i>Default: 000000</i>.</li> + * <li>al_errorcolor - [String] Hex formated color to use as foreground color on error. <i>Default: ff0000</i>.</li> + * <li>al_debug - [boolean] Whether to enable debug mode. <i>Default: false</i>.</li> + * </ul> + * </p> + * @author kappaOne + * @author Brian Matzon <br...@ma...> + * @version $Revision$ + * $Id$ + */ +public class AppletLoader extends Applet implements Runnable, AppletStub { + /** initializing */ + public static final int STATE_INIT = 1; + + /** determining which packages that are required */ + public static final int STATE_DETERMINING_PACKAGES = 2; + + /** checking for already downloaded files */ + public static final int STATE_CHECKING_CACHE = 3; + + /** downloading packages */ + public static final int STATE_DOWNLOADING = 4; + + /** extracting packages */ + public static final int STATE_EXTRACTING_PACKAGES = 5; + + /** updating the classpath */ + public static final int STATE_UPDATING_CLASSPATH = 6; + + /** switching to real applet */ + public static final int STATE_SWITCHING_APPLET = 7; + + /** initializing real applet */ + public static final int STATE_INITIALIZE_REAL_APPLET = 8; + + /** stating real applet */ + public static final int STATE_START_REAL_APPLET = 9; + + /** done */ + public static final int STATE_DONE = 10; + + /** used to calculate length of progress bar */ + protected int percentage; + + /** current size of download in bytes */ + protected int currentSizeDownload; + + /** total size of download in bytes */ + protected int totalSizeDownload; + + /** 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; + + /** progressbar to render while loading */ + protected Image progressbar; + + /** offscreen image used */ + protected Image offscreen; + + /** background color of applet */ + protected Color bgColor = Color.white; + + /** Color to write errors in */ + protected Color errorColor = Color.red; + + /** color to write forground in */ + protected Color fgColor = Color.black; + + /** urls of the jars to download */ + protected URL[] urlList; + + /** list of jars to download */ + protected String jarList; + + /** actual thread that does the loading */ + protected Thread loaderThread; + + /** animation thread that renders our loaderscreen while loading */ + protected Thread animationThread; + + /** applet to load after all downloads are complete */ + protected Applet lwjglApplet; + + /** whether a fatal error occured */ + protected boolean fatalError; + + /** fatal error that occured */ + protected String fatalErrorDescription; + + /** whether we're running in debug mode */ + protected boolean debugMode; + + /** String to display as a subtask */ + protected String subtaskMessage = ""; + + /** state of applet loader */ + protected int state = STATE_INIT; + + /** generic error message to display on error */ + protected String[] genericErrorMessage = { "An error occured while loading the applet.", + "Plese contact support to resolve this issue.", + "<placeholder for error message>"}; + + /* + * @see java.applet.Applet#init() + */ + public void init() { + + // sanity check + String[] requiredArgs = {"al_main", "al_logo", "al_progressbar", "al_jars"}; + for(int i=0; i<requiredArgs.length; i++) { + if(getParameter(requiredArgs[i]) == null) { + fatalErrorOccured("missing required applet parameter: " + requiredArgs[i]); + return; + } + } + + // whether to run in debug mode + debugMode = getBooleanParameter("al_debug", false); + + // get colors of applet + bgColor = getColor("al_bgcolor", Color.white); + fgColor = getColor("al_fgcolor", Color.black); + errorColor = getColor("al_errorcolor", Color.red); + + // load logos + logo = getImage("/" + getParameter("al_logo")); + progressbar = getImage("/" + getParameter("al_progressbar")); + + // jars to load + jarList = getParameter("al_jars"); + + //sanity check + if(logo == null || progressbar == null) { + fatalErrorOccured("Unable to load logo and progressbar images"); + return; + } + + // parse the urls for the jars into the url list + loadJarURLs(); + } + + /* + * @see java.applet.Applet#start() + */ + public void start() { + if(loaderThread == null && !fatalError) { + loaderThread = new Thread(this); + loaderThread.setName("AppletLoader.loaderThread"); + loaderThread.start(); + + animationThread = new Thread() { + public void run() { + while(state != STATE_DONE) { + 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(); + } + super.stop(); + } + + /* + * @see java.applet.Applet#destroy() + */ + public void destroy() { + if (lwjglApplet != null) { + lwjglApplet.destroy(); + } + + progressbar = null; + logo = null; + + super.destroy(); + } + + /** + * Retrieves the applet that has been loaded. Usefull for liveconnect. + */ + public Applet getApplet() { + return lwjglApplet; + } + + /* + * @see java.applet.AppletStub#appletResize(int, int) + */ + public void appletResize(int width, int height) { + /* uhm? */ + } + + /* + * @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 final synchronized void paint(Graphics g) { + + // paint applet if available + if(lwjglApplet != null && state == STATE_DONE) { + lwjglApplet.paint(g); + return; + } + + // create offscreen if missing + if (offscreen == null) { + offscreen = createImage(getWidth(), getHeight()); + } + + // draw everything onto an image before drawing to avoid flicker + Graphics og = offscreen.getGraphics(); + FontMetrics fm = og.getFontMetrics(); + + // set background color + og.setColor(bgColor); + og.fillRect(0, 0, getWidth(), getHeight()); + + // get logo position so its in the middle of applet + int x = 0, y = 0; + + if(logo != null && !fatalError) { + x = (getWidth() - logo.getWidth(this)) / 2; + y = (getHeight() - logo.getHeight(this)) / 2; + } + + og.setColor(fgColor); + String message = null; + + switch (state) { + case STATE_INIT: + message = "Initializing loader"; + break; + case STATE_DETERMINING_PACKAGES: + message = "Determining packages to load"; + break; + case STATE_CHECKING_CACHE: + message = "Checking cache for existing files"; + break; + case STATE_DOWNLOADING: + message = "Downloading packages"; + break; + case STATE_EXTRACTING_PACKAGES: + message = "Extracting downloaded packages"; + break; + case STATE_UPDATING_CLASSPATH: + message = "Updating classpath"; + break; + case STATE_SWITCHING_APPLET: + message = "Switching applet"; + break; + case STATE_INITIALIZE_REAL_APPLET: + message = "Initializing real applet"; + break; + case STATE_START_REAL_APPLET: + message = "Starting real applet"; + break; + case STATE_DONE: + message = "Done loading"; + break; + } + + if (fatalError) { + genericErrorMessage[genericErrorMessage.length-1] = fatalErrorDescription; + for(int i=0; i<genericErrorMessage.length; i++) { + int messageX = (getWidth() - fm.stringWidth(genericErrorMessage[i])) / 2; + int messageY = (getHeight() - (fm.getHeight() * genericErrorMessage.length)) / 2; + + og.setColor(errorColor); + og.drawString(genericErrorMessage[i], messageX, messageY + i*fm.getHeight()); + } + } else { + og.setColor(fgColor); + + // draw logo + og.drawImage(logo, x, y, null); + + // draw message + int messageX = (getWidth() - fm.stringWidth(message)) / 2; + int messageY = y + logo.getHeight(null) + 20; + og.drawString(message, messageX, messageY); + + // draw subtaskmessage, if any + if(subtaskMessage.length() > 0) { + messageX = (getWidth() - fm.stringWidth(subtaskMessage)) / 2; + og.drawString(subtaskMessage, messageX, messageY+20); + } + + // draw loading bar, clipping it depending on percentage done + int barSize = (progressbar.getWidth(this) * percentage) / 100; + og.clipRect(0, 0, x + barSize, getHeight()); + og.drawImage(progressbar, x, y, null); + } + + og.dispose(); + + // finally draw it all + g.drawImage(offscreen, 0, 0, null); + } + + /** + * 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() { + state = STATE_DETERMINING_PACKAGES; + + StringTokenizer jar = new StringTokenizer(jarList, ", "); + + int jarCount = jar.countTokens() + 1; + + urlList = new URL[jarCount]; + + try { + URL path = getCodeBase(); + + // set jars urls + for (int i = 0; i < jarCount - 1; i++) { + urlList[i] = new URL(path, jar.nextToken()); + } + + // native jar url + String osName = System.getProperty("os.name"); + String nativeJar = null; + + if (osName.startsWith("Win")) { + nativeJar = getParameter("al_windows"); + } else if (osName.startsWith("Linux") || osName.startsWith("FreeBSD") || osName.startsWith("SunOS")) { + nativeJar = getParameter("al_linux"); + } else if (osName.startsWith("Mac")) { + nativeJar = getParameter("al_mac"); + } else { + fatalErrorOccured("OS (" + osName + ") not supported"); + } + + if (nativeJar == null) { + fatalErrorOccured("no lwjgl natives files found"); + } else { + urlList[jarCount - 1] = new URL(path, nativeJar); + } + + } catch (MalformedURLException e) { + fatalErrorOccured(e.getMessage()); + } + } + + /** + * 4 steps + * + * 1) check version of applet and decide whether to download jars + * 2) download the jars + * 3) extract natives + * 4) add to jars to class path + * 5) switch applets + * + */ + public void run() { + + state = STATE_CHECKING_CACHE; + + percentage = 5; + + try { + if(debugMode) { + sleep(2000); + } + + // get path where applet will be stored + String path = (String) AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws Exception { + return System.getProperty("java.io.tmpdir") + File.separator + getParameter("al_title") + File.separator; + } + }); + + File dir = new File(path); + + // create directory + if (!dir.exists()) { + dir.mkdir(); + } + dir = new File(dir, "version"); + + // if applet already available don't download anything + boolean cacheAvailable = false; + + // version of applet + String version = getParameter("al_version"); + float latestVersion = 0; + + // if applet version specifed, check if you have latest version of applet + if (version != null) { + + latestVersion = Float.parseFloat(version); + + // if version file exists + if (dir.exists()) { + // compare to new version + if (latestVersion <= readVersionFile(dir)) { + cacheAvailable = true; + percentage = 90; + if(debugMode) { + sleep(2000); + } + } + } + } + + // if jars not available or need updating download them + if (!cacheAvailable) { + // downloads jars from the server + downloadJars(path); // 10-65% + + // Extracts Native Files + extractNatives(path); // 65-85% + + // add version information once jars downloaded successfully + if (version != null) { + percentage = 90; + writeVersionFile(dir, latestVersion); + } + } + + // add the downloaded jars and natives to classpath + updateClassPath(path); + + // switch to LWJGL Applet + switchApplet(); + + state = STATE_DONE; + } catch (AccessControlException ace) { + fatalErrorOccured(ace.getMessage()); + } catch (Exception e) { + fatalErrorOccured(e.getMessage()); + } finally { + loaderThread = null; + } + } + + /** + * read the current version file + * + * @param file the file to read + * @return the version value of saved file + * @throws Exception if it fails to read value + */ + protected float readVersionFile(File file) throws Exception { + DataInputStream dis = new DataInputStream(new FileInputStream(file)); + float version = dis.readFloat(); + dis.close(); + return version; + } + + /** + * write out version file of applet + * + * @param file the file to write out to + * @param version the version of the applet as a float + * @throws Exception if it fails to write file + */ + protected void writeVersionFile(File file, float version) throws Exception { + DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); + dos.writeFloat(version); + dos.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(String path) throws Exception { + + state = STATE_UPDATING_CLASSPATH; + + percentage = 95; + + Class[] parameters = new Class[] { URL.class}; + + // modify class path by adding downloaded jars to it + for (int i = 0; i < urlList.length; i++) { + // get location of jar as a url + URL u = new URL("file:" + path + getFileName(urlList[i])); + + // add to class path + Method method = URLClassLoader.class.getDeclaredMethod("addURL", parameters); + method.setAccessible(true); + method.invoke((URLClassLoader) ClassLoader.getSystemClassLoader(), new Object[] { u}); + } + + if(debugMode) { + sleep(2000); + } + + // 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"); + + // replace security system to avoid bug where vm fails to + // recognise downloaded jars as signed, when they are + System.setSecurityManager(null); + } + + /** + * replace the current applet with the lwjgl applet + * using AppletStub and initialise and start it + */ + protected void switchApplet() throws Exception { + + state = STATE_SWITCHING_APPLET; + percentage = 100; + + if(debugMode) { + sleep(2000); + } + + Class appletClass = Class.forName(getParameter("al_main")); + lwjglApplet = (Applet) appletClass.newInstance(); + + lwjglApplet.setStub(this); + + setLayout(new GridLayout(1, 1)); + add(lwjglApplet); + validate(); + + state = STATE_INITIALIZE_REAL_APPLET; + lwjglApplet.init(); + + state = STATE_START_REAL_APPLET; + lwjglApplet.start(); + } + + /** + * 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 { + + state = STATE_DOWNLOADING; + + URLConnection urlconnection; + + // calculate total size of jars to download + for (int i = 0; i < urlList.length; i++) { + urlconnection = urlList[i].openConnection(); + totalSizeDownload += urlconnection.getContentLength(); + } + + int initialPercentage = percentage = 10; + + // download each jar + byte buffer[] = new byte[65536]; + for (int i = 0; i < urlList.length; i++) { + if(debugMode) { + sleep(2000); + } + + urlconnection = urlList[i].openConnection(); + + String currentFile = getFileName(urlList[i]); + InputStream inputstream = urlconnection.getInputStream(); + FileOutputStream fos = new FileOutputStream(path + currentFile); + + + int bufferSize; + while ((bufferSize = inputstream.read(buffer, 0, buffer.length)) != -1) { + if(debugMode) { + sleep(10); + } + fos.write(buffer, 0, bufferSize); + currentSizeDownload += bufferSize; + percentage = initialPercentage + ((currentSizeDownload * 55) / totalSizeDownload); + subtaskMessage = "Retrieving: " + currentFile + " " + ((currentSizeDownload * 100) / totalSizeDownload) + "%"; + } + } + subtaskMessage = ""; + } + + /** + * This method will extract all file from the native jar and extract them + * to the subdirectory called "natives" in the local path, will also check + * to see if the native jar files is signed properly + * + * @param path base folder containing all downloaded jars + * @throws Exception if it fails to extract files + */ + protected void extractNatives(String path) throws Exception { + + state = STATE_EXTRACTING_PACKAGES; + + int initialPercentage = percentage; + + // get name of jar file with natives from urlList, it will be the last url + String nativeJar = getFileName(urlList[urlList.length - 1]); + + // get the current certificate to compare against native files + Certificate[] certificate = AppletLoader.class.getProtectionDomain().getCodeSource().getCertificates(); + + // create native folder + File nativeFolder = new File(path + "natives"); + if (!nativeFolder.exists()) { + nativeFolder.mkdir(); + } + + // open jar file + JarFile jarFile = new JarFile(path + nativeJar, true); + + // get list of files in jar + Enumeration entities = jarFile.entries(); + + totalSizeExtract = 0; + + // calculate the size of the files to extract for progress bar + while (entities.hasMoreElements()) { + JarEntry entry = (JarEntry) entities.nextElement(); + + // skip directories and anything in directories + // conveniently ignores the manifest + if (entry.isDirectory() || entry.getName().indexOf('/') != -1) { + continue; + } + totalSizeExtract += entry.getSize(); + } + + currentSizeExtract = 0; + + // reset point to begining by getting list of file again + entities = jarFile.entries(); + + // extract all files from the jar + while (entities.hasMoreElements()) { + JarEntry entry = (JarEntry) entities.nextElement(); + + // skip directories and anything in directories + // conveniently ignores the manifest + if (entry.isDirectory() || entry.getName().indexOf('/') != -1) { + continue; + } + + // check if native file already exists if so delete it to make room for new one + // useful when using the reload button on the browser + File f = new File(path + "natives" + File.separator + entry.getName()); + if (f.exists()) { + if (!f.delete()) { + continue; // unable to delete file, it is in use, skip extracting it + } + } + + if(debugMode) { + sleep(1000); + } + + InputStream in = jarFile.getInputStream(jarFile.getEntry(entry.getName())); + OutputStream out = new FileOutputStream(path + "natives" + File.separator + entry.getName()); + + int bufferSize; + byte buffer[] = new byte[65536]; + + while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) { + if(debugMode) { + sleep(10); + } + out.write(buffer, 0, bufferSize); + currentSizeExtract += bufferSize; + + // update progress bar + percentage = initialPercentage + ((currentSizeExtract * 20) / totalSizeExtract); + subtaskMessage = "Extracting: " + entry.getName() + " " + ((currentSizeExtract * 100) / totalSizeExtract) + "%"; + } + + // validate if the certificate for native file is correct + validateCertificateChain(certificate, entry.getCertificates()); + + in.close(); + out.close(); + } + subtaskMessage = ""; + + jarFile.close(); + } + + /** + * Validates the certificate chain for a single file + * + * @param ownCerts Chain of certificates to check against + * @param native_certs Chain of certificates to check + * @return true if the chains match + */ + protected static void validateCertificateChain(Certificate[] ownCerts, Certificate[] native_certs) throws Exception { + if (native_certs == null) + throw new Exception("Unable to validate certificate chain. Native entry did not have a certificate chain at all"); + + if (ownCerts.length != native_certs.length) + throw new Exception("Unable to validate certificate chain. Chain differs in length [" + ownCerts.length + " vs " + native_certs.length + "]"); + + for (int i = 0; i < ownCerts.length; i++) { + if (!ownCerts[i].equals(native_certs[i])) { + throw new Exception("Certificate mismatch: " + ownCerts[i] + " != " + native_certs[i]); + } + } + } + + /** + * Get Image from path provided + * + * @param s location of the image + * @return the Image file + */ + protected Image getImage(String s) { + try { + DataInputStream datainputstream = new DataInputStream(getClass().getResourceAsStream(s)); + byte abyte0[] = new byte[datainputstream.available()]; + datainputstream.readFully(abyte0); + datainputstream.close(); + return Toolkit.getDefaultToolkit().createImage(abyte0); + } catch (Exception e) { + /* */ + } + return null; + } + + + /** + * Get file name portion of URL. + * + * @param url Get file name from this url + * @return file name as string + */ + protected String getFileName(URL url) { + String fileName = url.getFile(); + return fileName.substring(fileName.lastIndexOf('/') + 1); + } + + /** + * Retrieves the color + * + * @param color Color to load + * @param defaultColor Default color to use if no color to load + * @return Color to use + */ + protected Color getColor(String color, Color defaultColor) { + String param_color = getParameter(color); + if (param_color != null) { + return new Color(Integer.parseInt(param_color, 16)); + } + return defaultColor; + } + + /** + * Retrieves the boolean value for the applet + * @param name Name of parameter + * @param defaultValue default value to return if no such parameter + * @return value of parameter or defaultValue + */ + protected boolean getBooleanParameter(String name, boolean defaultValue) { + String parameter = getParameter(name); + if(parameter != null) { + return Boolean.parseBoolean(parameter); + } + return defaultValue; + } + + /** + * Sets the state of the loaded and prints some debug information + * + * @param error Error message to print + * @param state State to enter + */ + protected void fatalErrorOccured(String error) { + fatalError = true; + fatalErrorDescription = "Fatal error occured (" + state + "): " + error; + System.out.println(fatalErrorDescription); + repaint(); + } + + /** + * Utility method for sleeping + * @param ms milliseconds to sleep + */ + protected void sleep(long ms) { + try { + Thread.sleep(ms); + } catch (Exception e) { + /* ignored */ + } + } +} \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |