/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq
* Copyright (C) 2013 Marko Zivkovic
*
* Contact Information: marko88zivkovic at gmail dot com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version. This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the
* GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
* This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
* during his Bachelor thesis at the computer science department of ETH Zurich,
* in the year 2013 and/or during future work.
*
* It is a reengineered version of XLogo written by Loic Le Coq, published
* under the GPL License at http://xlogo.tuxfamily.org/
*
* Contents of this file were initially written by Loic Le Coq,
* modifications, extensions, refactorings might have been applied by Marko Zivkovic
*/
/**
* Title : XLogo
* Description : XLogo is an interpreter for the Logo
* programming language
*
* @author Loïc Le Coq
*/
import java.io.*;
import java.util.StringTokenizer;
import java.util.Calendar;
/**
* @author loic
*
* This class extracts the file tmp_xlogo.jar from the main archive
* in the temporary file's directory
* and then launch the command:
* java -D-jar -Xmx64m -Djava.library.path=path_to_tmp -cp path_to_tmp
* tmp_xlogo.jar
*
* XLogo executes with a predefined heap size for the Virtual Machine *
*
* @author Marko Zivkovic
* The maximum heap size property is now fixed 128MB. In the future, the application preferences should be used.
* Command line arguments are ignored, because this application is GUI-based and for
* children. I see no reason for a GUI application to have command line arguments.
*/
public class Lanceur
{
private static String PROPERTIES_PREFIX = "ch.ethz.abz.xlogo4schools";
private static int DEFAULT_MEMORY_ALLOC = 128;
/**
* The process which contains the XLogo application
*/
private Process p;
/**
* The temporary folder which contains all files to start XLogo
*/
private File tmpFolder = null;
private File[] files = new File[12];
/**
* Main method
*
* @param args
* The path toward "lgo" files
*/
public static void main(String[] args)
{
new Lanceur();
}
Lanceur()
{
// Look for old files from XLogo crash
cleanTmp();
// Look for the memory that should be allocated to the JVM heap size
//Preferences prefs = Preferences.systemRoot().node(PROPERTIES_PREFIX); TODO this does not work ... :-(
//int memory = prefs.getInt("appMemory", DEFAULT_MEMORY_ALLOC);
int memory = DEFAULT_MEMORY_ALLOC;
// extract application in the java.io.tmp directory
extractApplication();
startApplicationProcess(memory);
restorePath();
deleteTmpFiles();
}
private void startApplicationProcess(int memoire)
{
try
{
// Add the tmp to the path
String newPath = tmpFolder.getAbsolutePath();
String javaLibraryPath = newPath + File.pathSeparatorChar + System.getProperty("java.library.path");
// Bug when launching under Windows with java webstart
javaLibraryPath = javaLibraryPath.replaceAll("\"", "");
System.out.println("Path: " + javaLibraryPath + "\n");
String[] commande = new String[5];
commande[0] = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
commande[1] = "-jar";
commande[2] = "-Xmx" + memoire + "m";
commande[3] = "-Djava.library.path=" + javaLibraryPath;
commande[4] = files[0].getAbsolutePath();
System.out.println("<----- Starting XLogo ---->");
String cmd = "";
for (int i = 0; i < commande.length; i++)
{
cmd += commande[i] + " ";
}
System.out.println(cmd + "\n\n");
p = Runtime.getRuntime().exec(commande);
// Receive Messages from the Process
startStreamForward(p.getInputStream());
startStreamForward(p.getErrorStream());
p.waitFor();
}
catch (Exception e)
{
System.out.println(e);
}
}
private void deleteTmpFiles()
{
System.out.println("Closing XLogo. Cleaning tmp file");
for (int i = 0; i < files.length; i++)
{
if (null != files[i])
files[i].delete();
}
tmpFolder.delete();
}
private void restorePath()
{
String pathToFolder = tmpFolder.getAbsolutePath();
String path = System.getProperty("java.library.path");
StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
String newPath = "";
while (st.hasMoreTokens())
{
if (!newPath.equals(""))
newPath += File.pathSeparator;
String element = st.nextToken();
if (!element.equals(pathToFolder))
newPath += element;
}
System.setProperty("java.library.path", newPath);
}
/**
* Used to catch application streams and write them to System.out
* @param stream
*/
private void startStreamForward(final InputStream stream)
{
new Thread(){
public void run()
{
BufferedReader reader = null;
try
{
reader = new BufferedReader(new InputStreamReader(stream));
String line = "";
while ((line = reader.readLine()) != null)
System.out.println(line);
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if (reader != null)
try
{
reader.close();
}
catch (IOException e)
{}
}
}
}.start();
}
/**
* This method checks for unused old XLogo files in temporary directory
* If it found files older than 24 hours with the prefix tmp_xlogo, these
* files are deleted.
*/
private void cleanTmp()
{
String path = System.getProperty("java.io.tmpdir");
File f = new File(path);
File[] files = f.listFiles();
if (null != files)
{
for (int i = 0; i < files.length; i++)
{
try
{
if (files[i].getName().startsWith("tmp_xlogo"))
{
long fileTime = files[i].lastModified();
long time = Calendar.getInstance().getTimeInMillis();
// Delete file if it's more than 24 hours old
if (time - fileTime > 24 * 3600 * 1000)
{
if (files[i].isDirectory())
deleteDirectory(files[i]);
files[i].delete();
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
/**
* This method extracts the file tmp_xlogo.jar from the archive and copy it
* into the temporary directory.
*/
private void extractApplication()
{
// Create in the "java.io.tmpdir" a directory called tmp_xlogo
int i = 0;
String tmpPath = System.getProperty("java.io.tmpdir") + File.separator + "tmp_xlogo";
while (true)
{
tmpFolder = new File(tmpPath + i);
if (!tmpFolder.exists())
break;
else
i++;
}
boolean b = tmpFolder.mkdir();
System.out.println("Creating tmp_xlogo directory - success: " + b);
// extract the file tmp_xlogo.jar in this folder
InputStream src = Lanceur.class.getResourceAsStream("tmp_xlogo.jar");
files[0] = new File(tmpFolder.getAbsolutePath() + File.separator + "tmp_xlogo.jar");
b = copier(src, files[0]);
System.out.println("Copying tmp_xlogo.jar - success: " + b);
// extract the file jh.jar in this folder
src = Lanceur.class.getResourceAsStream("jh.jar");
files[1] = new File(tmpFolder.getAbsolutePath() + File.separator + "jh.jar");
b = copier(src, files[1]);
System.out.println("Copying jh.jar - success: " + b);
// extract the file vecmath.jar in this folder
src = Lanceur.class.getResourceAsStream("vecmath.jar");
files[2] = new File(tmpFolder.getAbsolutePath() + File.separator + "vecmath.jar");
b = copier(src, files[2]);
System.out.println("Copying vecmath.jar - success: " + b);
// extract the file j3dcore.jar in this folder
src = Lanceur.class.getResourceAsStream("j3dcore.jar");
files[3] = new File(tmpFolder.getAbsolutePath() + File.separator + "j3dcore.jar");
b = copier(src, files[3]);
System.out.println("Copying j3dcore.jar - success: " + b);
// extract the file j3dutils.jar in this folder
src = Lanceur.class.getResourceAsStream("j3dutils.jar");
files[4] = new File(tmpFolder.getAbsolutePath() + File.separator + "j3dutils.jar");
b = copier(src, files[4]);
System.out.println("Copying j3dutils.jar - success: " + b);
// extract the file jl1.0.1 in this folder (JLayer library for mp3 playing)
src = Lanceur.class.getResourceAsStream("jl1.0.1.jar");
files[5] = new File(tmpFolder.getAbsolutePath() + File.separator + "jl1.0.1.jar");
b = copier(src, files[5]);
System.out.println("Copying jl1.0.1.jar - success: " + b);
// extract the file jl1.0.1 in this folder (JLayer library for mp3 playing)
src = Lanceur.class.getResourceAsStream("log4j-api-2.1.jar");
files[6] = new File(tmpFolder.getAbsolutePath() + File.separator + "log4j-api-2.1.jar");
b = copier(src, files[6]);
System.out.println("Copying log4j-api-2.1.jar - success: " + b);
// extract the file jl1.0.1 in this folder (JLayer library for mp3 playing)
src = Lanceur.class.getResourceAsStream("log4j-core-2.1.jar");
files[7] = new File(tmpFolder.getAbsolutePath() + File.separator + "log4j-core-2.1.jar");
b = copier(src, files[7]);
System.out.println("Copying log4j-core-2.1.jar - success: " + b);
// extract the native driver for java 3d in this folder
String os = System.getProperty("os.name").toLowerCase();
String arch = System.getProperty("os.arch");
System.out.println("Operating system: " + os);
System.out.println("Architecture: " + arch);
// Linux
if (os.indexOf("linux") != -1)
{
if (arch.indexOf("86") != -1)
{
InputStream lib = Lanceur.class.getResourceAsStream("linux/x86/libj3dcore-ogl.so");
files[8] = new File(tmpFolder.getAbsolutePath() + File.separator + "libj3dcore-ogl.so");
copier(lib, files[8]);
lib = Lanceur.class.getResourceAsStream("linux/x86/libj3dcore-ogl-cg.so");
files[9] = new File(tmpFolder.getAbsolutePath() + File.separator + "libj3dcore-ogl-cg.so");
copier(lib, files[9]);
}
else
{
InputStream lib = Lanceur.class.getResourceAsStream("linux/amd64/libj3dcore-ogl.so");
files[8] = new File(tmpFolder.getAbsolutePath() + File.separator + "libj3dcore-ogl.so");
copier(lib, files[8]);
}
}
// windows
else if (os.indexOf("windows") != -1)
{
if (arch.indexOf("86") != -1)
{
InputStream lib = Lanceur.class.getResourceAsStream("windows/x86/j3dcore-d3d.dll");
files[8] = new File(tmpFolder.getAbsolutePath() + File.separator + "j3dcore-d3d.dll");
b = copier(lib, files[8]);
System.out.println("Copying library 1 - success: " + b);
lib = Lanceur.class.getResourceAsStream("windows/x86/j3dcore-ogl.dll");
files[9] = new File(tmpFolder.getAbsolutePath() + File.separator + "j3dcore-ogl.dll");
b = copier(lib, files[9]);
System.out.println("Copying library 2 - success: " + b);
lib = Lanceur.class.getResourceAsStream("windows/x86/j3dcore-ogl-cg.dll");
files[10] = new File(tmpFolder.getAbsolutePath() + File.separator + "j3dcore-ogl-cg.dll");
b = copier(lib, files[10]);
System.out.println("Copying library 3 - success: " + b);
lib = Lanceur.class.getResourceAsStream("windows/x86/j3dcore-ogl-chk.dll");
files[11] = new File(tmpFolder.getAbsolutePath() + File.separator + "j3dcore-ogl-chk.dll");
b = copier(lib, files[11]);
System.out.println("Copying library 4 - success: " + b);
}
else
{
InputStream lib = Lanceur.class.getResourceAsStream("windows/amd64/j3dcore-ogl.dll");
files[8] = new File(tmpFolder.getAbsolutePath() + File.separator + "j3dcore-ogl.dll");
b = copier(lib, files[8]);
System.out.println("Copying library 1 - success: " + b);
}
}
// Mac os
else if (os.indexOf("mac") != -1)
{
}
// solaris
else if (os.indexOf("sunos") != -1)
{
if (arch.indexOf("86") != -1)
{
InputStream lib = Lanceur.class.getResourceAsStream("solaris/i386/libj3dcore-ogl.so");
files[8] = new File(tmpFolder.getAbsolutePath() + File.separator + "libj3dcore-ogl.so");
b = copier(lib, files[8]);
System.out.println("Copying library 1 - success: " + b);
}
else if (arch.indexOf("amd64") != -1)
{
InputStream lib = Lanceur.class.getResourceAsStream("solaris/amd64/libj3dcore-ogl.so");
files[8] = new File(tmpFolder.getAbsolutePath() + File.separator + "libj3dcore-ogl.so");
b = copier(lib, files[8]);
System.out.println("Copying library 1 - success: " + b);
}
}
}
/**
* This method copy the file tmp_xlogo.jar from the archive to the file
* Destination.
*
* @param destination
* The output file
* @return true if success, false otherwise
*/
private boolean copier(InputStream src, File destination)
{
boolean resultat = false;
// Declaration des flux
java.io.FileOutputStream destinationFile = null;
try
{
// Création du fichier :
destination.createNewFile();
// Ouverture des flux
destinationFile = new java.io.FileOutputStream(destination);
// Lecture par segment de 0.5Mo
byte buffer[] = new byte[512 * 1024];
int nbLecture;
while ((nbLecture = src.read(buffer)) != -1)
{
destinationFile.write(buffer, 0, nbLecture);
}
// Copie réussie
resultat = true;
}
catch (java.io.FileNotFoundException f)
{}
catch (java.io.IOException e)
{}
finally
{
// Quoi qu'il arrive, on ferme les flux
try
{
src.close();
}
catch (Exception e)
{}
try
{
destinationFile.close();
}
catch (Exception e)
{}
}
return (resultat);
}
/**
* Delete a the directory created by Logo in /tmp
*
* @param path
* The Directory path
* @return true if success
*/
private boolean deleteDirectory(File path)
{
boolean resultat = true;
if (path.exists())
{
File[] files = path.listFiles();
if (null != files)
{
for (int i = 0; i < files.length; i++)
{
if (files[i].isDirectory())
{
resultat &= deleteDirectory(files[i]);
}
else
{
resultat &= files[i].delete();
}
}
}
}
resultat &= path.delete();
return (resultat);
}
}