Problems with Jogl applet

I’m trying to convert my jogl webstart app to an applet. To test, I first tried to get the gears applet demo to work. I made an html page with the applet tag from https://jogl-demos.dev.java.net/applettest.html and put it on my server. That works. Then I tried putting the various .jar files locally. The server folder contains:
gears.html
gearsorg.html
jogl.jar
jogl-demos.jar
jogl-natives-linux-amd64.jar
jogl-natives-linux-i586.jar
jogl-natives-macosx-ppc.jar
jogl-natives-solaris-i586.jar
jogl-natives-solaris-sparc.jar
jogl-natives-solaris-sparcv9.jar
jogl-natives-windows-i586.jar

all taken from the right side of the jogl home page this morning (timestamp 2006-05-21 3:00)
joglorg.html has the original applet tag, jogl.html has codebase set to “.”

At first I was getting an error about jogl.dll being unsigned or having another problem, but now whenever I try to open the page I get:

java.security.AccessControlException: access denied (java.util.PropertyPermission user.home read)
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPropertyAccess(Unknown Source)
at java.lang.System.getProperty(Unknown Source)
at com.sun.opengl.util.JOGLAppletLauncher.start(JOGLAppletLauncher.java:288)
at sun.applet.AppletPanel.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Exception in thread “thread applet-com.sun.opengl.util.JOGLAppletLauncher” java.lang.NullPointerException
at sun.plugin.util.GrayBoxPainter.showLoadingError(Unknown Source)
at sun.plugin.AppletViewer.showAppletException(Unknown Source)
at sun.applet.AppletPanel.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

joglorg.html still works fine.

I tried searching the forum, and the only relevant thread I could find links to http://mindprod.com/jgloss/mime.html to test the mime types. Checking the jar files gives me
application/x-java-archive instead of the proper application/java-archive. However, the jar files at http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current have a text/plain mimetype so I can’t imagine that being the cause of the problem.

Anybody know what I’m doing wrong?

Hi,

Nightly builds of jogl aren’t signed. You have to download an official beta release to get rid of the security exceptions.

Lilian :slight_smile:

That did the trick. Thanks a lot. Now to figure out how to make an applet out of my program.

Is the applet supposed to only show the GLcanvas and not the rest of the Frame’s contents?

I replaced the contents of my main java file with those of gears and I’m gradually adding my own code back in. My UI consists of a frame with a gridbaglayout with some buttons at the top, a canvas in the middle and more buttons at the bottom.

When I run the jar file with java.exe it looks like it should, with the gears showing where the 3d scene used to be, surrounded by the rest of the UI. When I copy the jar file to the html directory and open the hml page it only shows the gears.

Is this the intended behaviour or am I missing something?

That isn’t the intended behavior. Keep in mind that your applet is the window in which other widgets will be added.

Couldn’t get it to work yet so I figured I’d try a bare-bones applet. Got the same error message as with the real application.

Applet code:

package lupine;

import java.applet.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;
import java.net.*;
import javax.media.opengl.glu.*;
import javax.media.opengl.*;
import com.sun.opengl.util.*;

public class TestApplet extends Applet implements GLEventListener {
	public static GLCanvas canvas = new GLCanvas();

	public void init(GLAutoDrawable drawable) {
		setLayout(null); 
		
		//Add an eventlistener to the canvas
		canvas.addGLEventListener(new TestApplet());

		final Animator animator = new Animator(canvas);

		Button button = new Button("Hello World");
		button.setBounds(20,20,80,30);
		add(button);

		canvas.setBounds(100,100,100,100);
		add(canvas);

		//start the animator
		animator.start();
	}

	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
	}


	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {}
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
}

html page:

<html>
<head>
</head>
<body bgcolor="#000000">
<applet code="com.sun.opengl.util.JOGLAppletLauncher"
     width=400
     height=300
     codebase="."
     archive="jogl.jar,TestApplet.jar">
  <param name="subapplet.classname" VALUE="lupine.TestApplet">
  <param name="subapplet.displayname" VALUE="Mini-makette Online">
  <param name="progressbar" value="true">
  <param name="cache_archive" VALUE="jogl.jar,TestApplet.jar">
  <param name="cache_archive_ex" VALUE="jogl.jar;preload,TestApplet.jar;preload">
</applet>
</body>
</html>

Error message:

Exception in thread "Thread-6" java.lang.UnsatisfiedLinkError: no jogl in java.library.path
	at java.lang.ClassLoader.loadLibrary(Unknown Source)
	at java.lang.Runtime.loadLibrary0(Unknown Source)
	at java.lang.System.loadLibrary(Unknown Source)
	at com.sun.opengl.impl.NativeLibLoader$DefaultAction.loadLibrary(NativeLibLoader.java:78)
	at com.sun.opengl.impl.NativeLibLoader.loadLibrary(NativeLibLoader.java:101)
	at com.sun.opengl.impl.NativeLibLoader.access$100(NativeLibLoader.java:47)
	at com.sun.opengl.impl.NativeLibLoader$1.run(NativeLibLoader.java:109)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.opengl.impl.NativeLibLoader.loadCore(NativeLibLoader.java:107)
	at com.sun.opengl.impl.windows.WindowsGLDrawableFactory.<clinit>(WindowsGLDrawableFactory.java:59)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Unknown Source)
	at javax.media.opengl.GLDrawableFactory.getFactory(GLDrawableFactory.java:106)
	at javax.media.opengl.GLCanvas.<init>(GLCanvas.java:113)
	at javax.media.opengl.GLCanvas.<init>(GLCanvas.java:82)
	at javax.media.opengl.GLCanvas.<init>(GLCanvas.java:75)
	at lupine.TestApplet.<clinit>(TestApplet.java:13)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Unknown Source)
	at com.sun.opengl.util.JOGLAppletLauncher.refreshJOGL(JOGLAppletLauncher.java:357)
	at com.sun.opengl.util.JOGLAppletLauncher.access$000(JOGLAppletLauncher.java:117)
	at com.sun.opengl.util.JOGLAppletLauncher$1.run(JOGLAppletLauncher.java:303)

Relevant directory contents:

testapplet.html
jogl.jar
jogl-natives-linux-i586.jar
jogl-natives-macosx-ppc.jar
jogl-natives-macosx-universal.jar
jogl-natives-solaris-i586.jar
jogl-natives-solaris-sparc.jar
jogl-natives-windows-i586.jar
TestApplet.jar

Obviously since it’s an applet I can’t give any parameters to start Java with. What am I doing wrong?

are you using a web server or just opening the html file directly ?

there might be problems with “file:///” URLs

just a guess

Lilian :slight_smile:

Was testing locally. I’ve put it on the webserver, but got the same result.
Any other ideas?

I’m sorry I’m lost… what exactly is your problem ?

I thought the first post (security exception) was solved, and your problems were now :

  • mainly : to have an applet showing your canvas and your other components
  • not as important : to work locally

Could you just explain again what your issues are now ?

Sorry if i’m a bit slow this morning…

Lilian :slight_smile:

Yep, those are the two goals. Both are currently being prevented by the library path error. In order to reduce other influences as much as possible I made that barebones applet so it would be easier to detect what the problem was. If I know how to get the small applet working the real one shouldn’t be too hard.

create your GLCanvas in the init() method of the applet.

currently you create it at applet construction time, and it prevent normal native loading to occur (which is done after the applet is created, but before invoking its init() method).

That should do the trick !

Lilian :slight_smile:

Had another look at the gears applet example and finally figured out why my UI isn’t showing up. The applet only defines a canvas that gets the rendering instructions from the real code. Recreating the UI in the applet would require re-defining it in the applet and changing all the references in the normal code. While this would be possible, the problem is the program will be distributed as an applet, a webstart application and as an offline version for CD distributions. The webstart and offline version use the same code, but if the applet requires different code this makes changing code a rather annoying process since you have to do everything twice.

I don’t suppose there’s any way to have the applet do nothing more than simply embedding the webstart application into a web page? I tried the following code:

package lupine;

import java.applet.*;
import java.awt.*;
import java.io.*;

import lupine.Lupine;

public class TestApplet extends Applet {
	public void init() {
		setLayout(new BorderLayout());

		add(lupine.Lupine.ui, BorderLayout.CENTER);
	}
}

(Lupine is the name of the program, ui returns a Frame).

This got me the following error message:

java.lang.IllegalArgumentException: adding a window to a container
	at java.awt.Container.addImpl(Unknown Source)
	at java.awt.Container.add(Unknown Source)
	at lupine.TestApplet.init(TestApplet.java:13)
	at com.sun.opengl.util.JOGLAppletLauncher.startSubApplet(JOGLAppletLauncher.java:721)
	at com.sun.opengl.util.JOGLAppletLauncher.access$700(JOGLAppletLauncher.java:117)
	at com.sun.opengl.util.JOGLAppletLauncher$2.run(JOGLAppletLauncher.java:685)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)

I realise that what I want probably isn’t possible but I figured I’d ask anyway on the off chance that it is.

Refactor your application to be “contained” in a JPanel instead of a JFrame.

Then you just have a small wrapper class to embed it in JWS, Applet or standalone application.

Lilian

Thanks, that seems to (mostly) work.

The problem is that JPanel doesn’t support the pack() method that a Frame has which messes up part of my interface.
In the web start version the normal UI (buttons, logo graphic, etc.) look good but the JEditorPane I use does not behave correctly. The size seems to be set correctly whenever it’s resized but the text on it never shows up. In the applet the buttons and logo graphic at the top are shown twice (once in the correct location, once centered horizontally with the .weightx = 1 whitespace between them having 0 width) in addition to the JEditorPane behaviour.

Is there any way to mimic pack() behaviour for the JPanel?

validate() should work fine

Lilian :slight_smile:

Aha. Hurray for conistent method names :slight_smile:

It worked for the webstart version, but not for the applet. Have put a validate in the method that returns the panel, in the method that resizes the JEditorPane and one more in the applet for good measure. Starting the applet in a fresh browser shows a correct looking UI, though still with the double buttons and logo (What I find strangest is that both sets of buttons work. A drawing artifact I could understand). The JEditorPane isn’t sized correctly. Refreshing the html page will show only the bottom part of the UI and the JEditorPane, and sometimes the top part of the UI (buttons, logo). Subsequent refreshes seem to move these components horizontally, with the left and right edges of the applet wrapping around so that for example the left part of the bottom bar is on the right side of the screen and the right part on the left. The GLCanvas never shows up.

current applet code:

package lupine;

import java.applet.*;
import java.awt.*;
import java.io.*;

import javax.swing.*;
import javax.media.opengl.*;
import com.sun.opengl.util.*;
import lupine.Lupine;
import lupine.UI;

public class TestApplet extends Applet {

	private Animator animator;
	private JPanel panel;

	public void init() {
		setLayout(new BorderLayout());

		panel = UI.getPanel();
		panel.setSize(getSize());

		add(panel, BorderLayout.CENTER);

		panel.validate();

		Lupine.canvas.addGLEventListener(new Lupine());
		animator = new FPSAnimator(Lupine.canvas, 60);
	}

	public void start() {
		animator.start();
	}

	public void stop() {
		animator.stop();
	}

}

try and invoke validate() on the applet, not the panel (it will cascade)

Lilian

Nope, same result.

In case it will help, here’s the code for UI.java:

//---------------------------------------------
// UI
//---------------------------------------------
// Last modified: May 4 2006
//
// This class defines the user interface
//---------------------------------------------

package lupine;

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import javax.swing.*;
import javax.swing.text.html.*;
import java.lang.Math.*;
import java.beans.*;
import java.text.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;

public class UI {
	private static Scene scene = Scene.getInstance();
	private static Render render = Render.getInstance();
	
	private static int htmlSize;
	//The format of prices in the program
	private static final DecimalFormat currencyFormat = new DecimalFormat("###,###,###,###.00");
	
	public static JPanel frame = new JPanel();
	public static Checkbox walk, fly;
	public static ImageButton keyA, keyZ, title;
	public static JEditorPane htmlPane;
	
	//Specify the html escape and regex code for the currency symbol (in this case the euro)
	public static final String currencyHtml = "€";
	public static final String currencyJava = "\u20AC";
	
	//Hardcoded html strings
	public static final String loadScreen = "<html><head><style><!-- body { margin-top: 0px; background: #FFFFFF; } " +
		"h1 { color: black; font-family: Arial, Helvetica; font-size: 20pt; }--></style></head><body>" +
		"



<table width='100%'><tr><td align='center'>" +
		"<h1>De maquette wordt geladen.
Een ogenblik geduld a.u.b.</h1></td></tr></table></body></html>";
	public static final String priceString = "Totale prijs tot nu toe: ";
	public static final String titleStart = "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tr><td><h1>";
	public static final String titleEnd = "</h1></td><td align=\"right\" valign=\"top\"><a href=\"../blank.html\">" +
		"<img src=\"../close.gif\" border=\"0\"></a></td></tr></table>";
	
	public static synchronized JPanel getPanel() {
		//Load the user interface
		loadGUI();
		
		//Pack the JPanel
		frame.validate();

		//Maximize the html panel
		UI.showHtml(2);
		
		return frame;
	}
	
	//Return the state of the html pane
	//0 = hidden, 1 = normal, 2 = maximized
	public static synchronized int getHtmlSize() {
		return htmlSize;
	}
	
	//Set the state of the html pane
	public static synchronized void setHtmlSize(int size) {
		htmlSize = size;
	}
	
	//Place GUI compments like menus and buttons on the supplied frame
	private static void loadGUI() {
		final Color background = new Color(255, 255, 255);
		URL imgURL;
		
		//Show tooltips after 10 ms instead of the default 750 ms
		ToolTipManager.sharedInstance().setInitialDelay(10);
		
		//Set the frame colors
		frame.setBackground(background);
		frame.setForeground(new java.awt.Color(0, 0, 0));
		
		//Use a gridbag layoutmanager
		frame.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		//Let components completely fill the cells
		c.fill = GridBagConstraints.BOTH;

		//Title panel
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 2;
		c.weightx = 0.0;
		c.weighty = 0.0;
		c.insets = new Insets(15, 50, 0, 50);
		Panel titlePanel = new Panel();
		frame.add(titlePanel, c);
		
		//Add content to title panel
			//Again, use a gridbag layoutmanager
			titlePanel.setLayout(new GridBagLayout());
			GridBagConstraints ct = new GridBagConstraints();
			ct.anchor = GridBagConstraints.NORTHWEST;
			
			ct.gridx = 0;
			ct.gridy = 0;
			imgURL = UI.class.getResource(Lupine.uiFilePath + "buttonhelp.gif");
			ImageButton buttonHelp = new ImageButton(imgURL, imgURL, 0, Lupine.htmlFilePath + "help.html");
			buttonHelp.setCursor(new Cursor(Cursor.HAND_CURSOR));
			buttonHelp.setToolTipText("Help");
			titlePanel.add(buttonHelp, ct);

			ct.gridx = 1;
			titlePanel.add(new Label(" "), ct);
			
			ct.gridx = 2;
			imgURL = UI.class.getResource(Lupine.uiFilePath + "buttoninfo.gif");
			ImageButton buttonInfo = new ImageButton(imgURL, imgURL, 0, Lupine.htmlFilePath + "info.html");
			buttonInfo.setCursor(new Cursor(Cursor.HAND_CURSOR));
			buttonInfo.setToolTipText("Info");
			titlePanel.add(buttonInfo, ct);
			
			ct.gridx = 3;
			titlePanel.add(new Label(" "), ct);
			
			ct.gridx = 4;
			imgURL = UI.class.getResource(Lupine.uiFilePath + "buttonadvanced.gif");
			ImageButton buttonAdvanced = new ImageButton(imgURL, imgURL, 0, Lupine.htmlFilePath + "options.html");
			buttonAdvanced.setCursor(new Cursor(Cursor.HAND_CURSOR));
			buttonAdvanced.setToolTipText("Geavanceerde opties");
			titlePanel.add(buttonAdvanced, ct);
			
			ct.gridx = 5;
			ct.weightx = 1.0;
			titlePanel.add(new Label(" "), ct);
			
			ct.gridx = 6;
			ct.weightx = 0.0;
			ct.gridheight = 2;
			ct.anchor = GridBagConstraints.EAST;
			imgURL = UI.class.getResource(Lupine.uiFilePath + "title.gif");
			title = new ImageButton(imgURL, imgURL, 0, "");
			titlePanel.add(title, ct);
			
			ct.gridx = 0;
			ct.gridy = 1;
			ct.gridheight = 1;
			ct.gridwidth = 5;
			ct.anchor = GridBagConstraints.WEST;
			imgURL = UI.class.getResource(Lupine.uiFilePath + "bbvh.gif");
			ImageButton logo = new ImageButton(imgURL, imgURL, 0, "");
			titlePanel.add(logo, ct);
		//
		
		//OpenGL Canvas
		c.gridy = 1;
		c.gridwidth = 1;
		c.weightx = 1.0;
		c.weighty = 1.0;
		c.insets = new Insets(5, 50, 0, 0);
		frame.add(Lupine.canvas, c);
		
		//HTML Pane
		c.gridx = 1;
		c.weightx = 0.0;
		c.weighty = 1.0;
		c.insets = new Insets(5, 0, 0, 50);
		c.anchor = GridBagConstraints.NORTHWEST;
		htmlPane = new JEditorPane("text/html", "");
		//Set the loading html page directly since loading it with setPage takes too long on old computers,
		//which prevents it from getting displayed at all
		UI.htmlPane.setText(loadScreen);
		
		htmlPane.setBackground(background);
		htmlPane.setBorder(BorderFactory.createEmptyBorder());
		htmlPane.setEditable(false);
		htmlPane.addHyperlinkListener(new MyHyperlinkListener());
		
		//Html pages are loaded asynchronously. This code fires whenever a page is fully loaded
		htmlPane.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent e) {
				if(e.getPropertyName().equals("page")) {
					//Apply post-processing to the page
					processHtml();
				}
			}
		});
		
		
		frame.add(htmlPane, c);
		
		//Flight panel
		c.gridx = 0;
		c.gridy = 2;
		c.gridwidth = 2;
		c.weightx = 0.0;
		c.weighty = 0.0;
		c.insets = new Insets(25, 50, 25, 50);
		c.anchor = GridBagConstraints.CENTER;
		Panel flightPanel = new Panel();
		frame.add(flightPanel, c);
		
		//Add content to flightpanel
...Snipped...
		//
	}
	
	//Display a html page in the html pane
	public static boolean loadHtml(String target) {
		URL location = UI.class.getResource("/" + target);
		
		//Determine if the frame should be hidden
		if (target.equals("")) {
			//Hide the html pane
			showHtml(0);
			location = UI.class.getResource("/" + Lupine.htmlFilePath + "blank.html");
		} else {
			//Show the html pane at regular size
			showHtml(1);
		}

		//Try to display the page
		try{
			htmlPane.setPage(location);
		} catch(Exception eu){
			htmlPane.setText("Could not load page");
			//Loading the page failed
			return false;
		}
		
		//Loading the page succeeded
		return true;
	}
	
	//Show or hide the HTML component
	public static void showHtml(int size) {
		int height = (int)Math.max(Lupine.canvas.getHeight(), UI.htmlPane.getSize().getHeight());
		int fullWidth = (int)UI.frame.getSize().getWidth() - 100;
		
		//Record which state the html pane is in
		setHtmlSize(size);
		
		//Hide it
		if (size == 0) {
			//Resize the viewport to the full screen width
			Lupine.canvas.setSize(new Dimension(fullWidth, height));
			UI.htmlPane.setPreferredSize(new Dimension(0, height));
		}
		//Show it at regular size
		if (size == 1) {
			//Resize the viewport to the full screen width
			Lupine.canvas.setSize(new Dimension(fullWidth - (int)UI.title.getSize().getWidth() - 10, height));
			UI.htmlPane.setPreferredSize(new Dimension((int)UI.title.getSize().getWidth() + 10, height));
		}
		//Show it maximized
		if (size == 2) {
			//Resize the viewport to the full screen width
			Lupine.canvas.setSize(new Dimension(0, height));
			UI.htmlPane.setPreferredSize(new Dimension(fullWidth, height));
		}
		
		//Pack the JPanel
		UI.frame.validate();

		//Set the focus to the canvas so there's no selection box around the first radio button
		Lupine.canvas.requestFocusInWindow();
		
		render.setWantRender(true);
	}
}

validate() must be called when the hierarchy is complete (there’s no need to call it in the method that returns the panel… just at the end of applet init() ) .

You use static members in your UI class, so every time the applet page is refreshed, the init()/start() method are invoked, but are still applied to your static members… which should be growing and growing at each refresh…

The best way to avoid that would be to use instance variables, or to clean up everything on the applet destroy() method to allow for a fresh reload.

Applet coding == avoid static variables or manage very carefully their life cycle…

Lilian :slight_smile:

Ok, slowly but surely getting there. I figured out I can make things a whole lot of problems by simply instantiating the main class. Currently the program works perfectly when it is loaded in a fresh browser. The only problem is that repainting the UI, say after moving another window over it, isn’t working (the GLCanvas is repainted by the animator). Refreshing will now display a correct UI, but the GLCanvas remains blank and the console throws some openGL related errors, so my destruction code isn’t working correctly yet.

Current applet code:

package lupine;

import java.applet.*;
import java.awt.*;
import java.io.*;

import javax.swing.*;
import javax.media.opengl.*;
import com.sun.opengl.util.*;
import lupine.Lupine;
import lupine.UI;

public class TestApplet extends Applet {

	private Animator animator;
	private Lupine lupine = new Lupine();

	public void init() {
		setLayout(new BorderLayout());

		add(lupine.ui, BorderLayout.CENTER);

		lupine.canvas.addGLEventListener(lupine);
		animator = new FPSAnimator(lupine.canvas, 60);
	}

	public void update(Graphics g) {
		repaint();
	}

	public void start() {
		animator.start();
	}

	public void stop() {}

	public void destroy() {
		animator.stop();
		lupine.canvas.removeGLEventListener(lupine);
		remove(lupine.ui);
		animator = null;
		lupine = null;
	}
}

As far as I understood removing all references to an object and setting it to null should make the garbage collector clean it up, but the GC seems to disagree. Also calling repaint (or paint(g); ) in update is apparently not the correct way to redraw the applet. Any tips?

Thanks again for all the help.