/**
*
*/
package cz.cuni.mff.peckam.java.origamist.gui.common;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessControlException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import cz.cuni.mff.peckam.java.origamist.utils.ExtensionClassLoader;
/**
* This bootstrapping applet loads native Java3D libraries without the need of installing Java3D to the client machine.
*
* Then it loads the contained applet using its own classloader, which is aware of the Java3D native libraries.
*
* @author Martin Pecka
*/
public abstract class Java3DBootstrappingApplet extends JApplet
{
/** */
private static final long serialVersionUID = 8579150219023896739L;
/** The bootstrapped applet. */
private JApplet appletApplication;
/** Set this to true if the applet is being loaded as a standard java application from the command line. */
protected boolean launchedAsApplication = false;
/** If this applet is loaded as a standard Java application from the command line, this array holds its arguments. */
protected String[] args = null;
/** The map of parameters derived from application arguments, if launched as application. */
private Map<String, String> parameters = null;
/** The window this applet is displayed in, if launched as application. */
protected JFrame window = null;
{
System.setSecurityManager(null);
}
@Override
public void init()
{
if (!isJava5OrSuperior()) {
showError("<html><p>This applet may be run under Windows, Mac OS X 10.4+, Linux and Solaris. "
+ "<br>On other systems, Java3D must be installed from https://java3d.dev.java.net/binary-builds.html. "
+ "<br>It requires Java version 5 or superior.</p>");
} else {
if (launchedAsApplication) {
parameters = new HashMap<String, String>();
if (args != null) {
for (String arg : args) {
if (arg.contains("=")) {
String[] tokens = arg.split("=", 2);
parameters.put(tokens[0], tokens[1]);
}
}
} else {
System.err
.println("The bootstrapper has been launched as a standard application, but the arguments"
+ "array (this.args) isn't set. Assuming no parameters were passed.");
}
}
createAppletApplication();
this.appletApplication.init();
}
}
@Override
public void start()
{
super.start();
this.appletApplication.start();
}
@Override
public void stop()
{
super.stop();
this.appletApplication.stop();
}
@Override
public void destroy()
{
if (this.appletApplication != null) {
try {
Method destroyMethod = this.appletApplication.getClass().getMethod("destroy", new Class[0]);
destroyMethod.invoke(this.appletApplication, new Object[0]);
} catch (Exception ex) {
// Can't do better than print stack trace when applet is destroyed
ex.printStackTrace();
}
}
this.appletApplication = null;
// Collect deleted objects (seems to be required under Mac OS X when the applet is being reloaded)
System.gc();
}
/**
* Returns <code>true</code> if current JVM version is 5+.
*/
protected boolean isJava5OrSuperior()
{
String javaVersion = System.getProperty("java.version");
String[] javaVersionParts = javaVersion.split("\\.|_");
if (javaVersionParts.length >= 1) {
try {
// Return true for Java SE 5 and superior
if (Integer.parseInt(javaVersionParts[1]) >= 5) {
return true;
}
} catch (NumberFormatException ex) {}
}
return false;
}
@Override
public URL getDocumentBase()
{
if (!launchedAsApplication) {
return super.getDocumentBase();
} else {
try {
return new File("").getAbsoluteFile().toURI().toURL();
} catch (MalformedURLException e) {
try {
return new URL(System.getProperty("user.dir"));
} catch (MalformedURLException e1) {
return null;
}
}
}
}
@Override
public URL getCodeBase()
{
if (!launchedAsApplication) {
return super.getCodeBase();
} else {
URL location = getClass().getProtectionDomain().getCodeSource().getLocation();
// if the URL ends with a filename, remove it, so that code base is a directory
try {
location = new URL(location, ".");
} catch (MalformedURLException e) {}
return location;
}
}
@Override
public String getParameter(String name)
{
if (!launchedAsApplication) {
return super.getParameter(name);
} else {
if (parameters == null)
return null;
return parameters.get(name);
}
}
/**
* A utility method that launches the given applet if the program has been started as Java application.
*
* @param applet The applet to launch.
* @param frameTitle Title of the window containing the applet.
* @param args Command-line arguments used when starting the application. If arguments width=xxx and height=yyy are
* set, they are used as the default size of the frame.
*/
public static void main(final Java3DBootstrappingApplet applet, String frameTitle, String[] args)
{
applet.launchedAsApplication = true;
applet.args = args;
JFrame window = new JFrame(frameTitle);
applet.window = window;
window.setContentPane(applet);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e)
{
applet.stop();
applet.destroy();
}
});
window.pack();
applet.init();
applet.start();
if (applet.getParameter("width") != null && applet.getParameter("height") != null) {
try {
int width = Integer.parseInt(applet.getParameter("width"));
int height = Integer.parseInt(applet.getParameter("height"));
window.setSize(width, height);
} catch (NumberFormatException e) {
}
}
window.setVisible(true);
}
/**
* Shows the given text in a label.
*/
protected void showError(String text)
{
JLabel label = new JLabel(text, JLabel.CENTER);
setContentPane(label);
}
/**
* Creates a new <code>AppletApplication</code> instance that manages this applet content.
*/
protected void createAppletApplication()
{
try {
String os = System.getProperty("os.name");
if (os == null)
os = "Windows";
String systemBits = System.getProperty("sun.arch.data.model");
if (!"32".equals(systemBits) && !"64".equals(systemBits))
systemBits = "32";
String arch = System.getProperty("os.arch");
if (arch == null)
arch = "x86";
// we must load the JAR libraries with this classloader too, because they load the native libraries
// using the classloader they were loaded with
List<String> java3DFiles = new LinkedList<String>(Arrays.asList(new String[] { "lib/j3dcore.jar",
"lib/vecmath.jar", "lib/j3dutils.jar" }));
if (os.startsWith("Windows") && systemBits.startsWith("32")) {
java3DFiles.add("lib/bin/windows-i586/j3dcore-d3d.dll");
java3DFiles.add("lib/bin/windows-i586/j3dcore-ogl.dll");
java3DFiles.add("lib/bin/windows-i586/j3dcore-ogl-cg.dll");
java3DFiles.add("lib/bin/windows-i586/j3dcore-ogl-chk.dll");
} else if (os.startsWith("Windows") && systemBits.startsWith("64")) {
java3DFiles.add("lib/bin/windows-amd64/j3dcore-ogl.dll");
} else if (os.startsWith("Linux") && systemBits.startsWith("32")) {
java3DFiles.add("lib/bin/linux-i586/libj3dcore-ogl.so");
java3DFiles.add("lib/bin/linux-i586/libj3dcore-ogl-cg.so");
} else if (os.startsWith("Linux") && systemBits.startsWith("64")) {
java3DFiles.add("lib/bin/linux-amd64/libj3dcore-ogl.so");
} else if (os.startsWith("Linux") && systemBits.startsWith("32")) {
java3DFiles.add("lib/bin/linux-i586/libj3dcore-ogl.so");
} else if (os.startsWith("SunOS") && arch.startsWith("sparcv9")) {
java3DFiles.add("lib/bin/solaris-sparc-v9/libj3dcore-ogl.so");
} else if (os.startsWith("SunOS") && arch.startsWith("sparc")) {
java3DFiles.add("lib/bin/solaris-sparc/libj3dcore-ogl.so");
} else if (os.startsWith("SunOS") && (arch.startsWith("x86_64") || arch.startsWith("amd64"))) {
java3DFiles.add("lib/bin/solaris-x86-amd64/libj3dcore-ogl.so");
} else if (os.startsWith("SunOS") && arch.startsWith("x86")) {
java3DFiles.add("lib/bin/solaris-x86/libj3dcore-ogl.so");
} else if (os.startsWith("Max OS X") && arch.startsWith("ppc")) {
java3DFiles.add("lib/bin/macosx-ppc/libjogl.jnilib");
java3DFiles.add("lib/bin/macosx-ppc/libjogl_awt.jnilib");
java3DFiles.add("lib/bin/macosx-ppc/libjogl_cg.jnilib");
java3DFiles.add("lib/bin/macosx-ppc/libgluegen-rt.jnilib");
java3DFiles.add("lib/gluegen-rt.jar");
} else if (os.startsWith("Max OS X")) {
java3DFiles.add("lib/bin/macosx-universal/libjogl.jnilib");
java3DFiles.add("lib/bin/macosx-universal/libjogl_awt.jnilib");
java3DFiles.add("lib/bin/macosx-universal/libjogl_cg.jnilib");
java3DFiles.add("lib/bin/macosx-ppc/libgluegen-rt.jnilib");
java3DFiles.add("lib/gluegen-rt.jar");
} else {
// fallback to 32bit windows
java3DFiles.add("lib/bin/windows-i586/j3dcore-d3d.dll");
java3DFiles.add("lib/bin/windows-i586/j3dcore-ogl.dll");
java3DFiles.add("lib/bin/windows-i586/j3dcore-ogl-cg.dll");
java3DFiles.add("lib/bin/windows-i586/j3dcore-ogl-chk.dll");
JOptionPane.showMessageDialog(rootPane,
"<html>Your system was not recognized by the origami application. You may either "
+ "run the <br>application on a supported system (Windows, Mac OS X 10.4+, Linux and "
+ "Solaris both in 32 and 64 versions) or <br>you must install Java3D manually for "
+ "your system. You may find the appropriate download on "
+ "<br>https://java3d.dev.java.net/binary-builds.html</html>");
}
List<String> applicationPackages = new LinkedList<String>(Arrays.asList(new String[] { "javax.media",
"javax.vecmath", "com.sun.j3d", "com.sun.opengl", "com.sun.gluegen.runtime", "javax.media.opengl",
"com.sun.media", "com.ibm.media", "jmpapps.util", "com.microcrowd.loader.java3d", "org.sunflow" }));
applicationPackages.addAll(Arrays.asList(getApplicationRootPackages()));
ClassLoader extensionsClassLoader = new ExtensionClassLoader(this.getClass().getClassLoader(), this
.getClass().getProtectionDomain(), java3DFiles, applicationPackages, getCodeBase());
// Call application constructor with reflection
Class<?> applicationClass = extensionsClassLoader.loadClass(getApplicationClassName());
Constructor<?> applicationConstructor = applicationClass.getConstructor(new Class[] { JApplet.class });
this.appletApplication = (JApplet) applicationConstructor.newInstance(new Object[] { this });
} catch (Throwable ex) {
if (ex instanceof InvocationTargetException) {
ex = ((InvocationTargetException) ex).getCause();
}
if (ex instanceof AccessControlException) {
showError("<html>If you want to run this applet, you must relaunch your browser,"
+ "<br>display this page again and accept the displayed digital signature.");
} else {
showError("<html>Can't start applet:<br>Exception" + ex.getClass().getName() + " " + ex.getMessage());
ex.printStackTrace();
}
}
}
/**
* @return The window the application runs in, if launched as application.
*/
public JFrame getWindow()
{
return window;
}
/**
* Returns the name of the application class associated to this applet.
* This class must have a constructor taking in parameter a <code>JApplet</code>.
*/
protected abstract String getApplicationClassName();
/**
* Returns the roots of the packages the bootstrapped application consists of.
*
* Eg. if your app has packages <code>my.app, my.app.gui, my.app.tools, my.app.tools.useless, javax.my,
* javax.my.tools</code>, this should return <code>{"my.app", "javax.my"}</code>
*
* @return The roots of the packages the bootstrapped application consists of.
*/
protected abstract String[] getApplicationRootPackages();
}