SWT Applikation als Applet

de

Neulich stellte sich mir die Frage, warum eigentlich nicht auch SWT Applikationen als Applet laufen können. Gut der erste Grund ist sicherlich, dass SWT nicht immer installiert ist, im Gegensatz zu Swing, was schon mit dem JRE dabei ist. Allerdings ist das so dramatisch auch wieder nicht in Zeiten schneller DSL-Leitungen, dann wird’s eben nachgeladen.

Was aber noch dazu kommt: SWT braucht native Libraries und native Libraries lassen sich nicht so einfach über den ClassLoader nachladen, wie irgendwelche Java-Klassen.

Ist das Applet allerdings signiert, dann ist auch das kein Problem und was dann mit relativ wenig Aufwand möglich ist …

Das Bespiel zeigt ein SWT Demo von IBM: den FileViewer. Die Klasse ist so gut wie nicht geändert worden, lediglich die Initialisierung der Shell muss etwas anders aussehen.

Dann habe ich noch einen kleinen Bootstrap-Loader für SWT geschrieben (bislang nur für Windows) und schon startet ein SWT Applet.

Achtung: Das Applet ist zwar nur ein Demo, aber der FileViewer ist echt: z.B. Doppelklick auf ein Word-Dokument startet Word!!

Bemerkung: das Applet ist signiert und erhält nach Bestätigung das Recht SWT zu installieren. Dazu werden zwei DLLs heruntergeladen und auf dem Java-Library-Path gespeichert. Sollte dies auf Grund unzureichender Rechte des angemeldeten Benutzers nicht funktionieren, so startet das Demo nicht korrekt und liefert stattdessen einen Fehler.

Code:

package com.rinke.solutions;

import java.applet.Applet;
import java.awt.Canvas;
import java.awt.FlowLayout;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import javax.swing.JOptionPane;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.examples.fileviewer.FileViewer;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SWTApplet extends Applet {

@Override
public void init() {

super.init();
try {
Runtime.getRuntime().loadLibrary(“swt-win32-3139.dll”);
} catch( Throwable e) {
System.out.println(“Load Library failed” );
String path = System.getProperty
(“java.library.path”);
String
[] paths = path.split(File.pathSeparator);
String targetDir =
null;
for (int i = 0; i < paths.length; i++) {
String t = paths[i];
if( testWriteable(t) ) {
targetDir = t;
break;
}
}
if( targetDir != null ) {
download( “swt-win32-3139.dll”, targetDir );
download
( “swt-awt-win32-3139.dll”, targetDir );
} else {
JOptionPane.showMessageDialog(this,“Could not download swt dlls, java.library.path not writeable”,
“Error Downloading DLLs”, JOptionPane.ERROR_MESSAGE);
}
}

resize(640, 400);
final Canvas c = new Canvas();
setLayout
(new FlowLayout());
c.setSize
(640, 400);
add
(c);

new Thread( new Runnable() {

public void run() {
createSWT(c);
}

}).start();
}

private boolean testWriteable(String dir) {
try {
FileOutputStream fos = new FileOutputStream( dir + File.pathSeparator + “test” );
fos.write
(1);
fos.close
();
File f =
new File( dir + File.pathSeparator + “test” );
f.delete
();
return true;
} catch( Exception e){

}
return false;
}

private void download(String filename, String targetDir) {
try {
URL url = new URL( getCodeBase() + filename );
InputStream is = url.openStream
();
File of =
new File(targetDir + “/” + filename);
FileOutputStream o =
new FileOutputStream( of );
System.out.println
(“downloading from ” + url + “to ” + of.getAbsolutePath());
byte[] buffer = new byte[10000];
while( true ) {
int c = is.read(buffer);
if( c == -1 ) break;
o.write
(buffer,0,c);
}
o.close();
is.close
();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

Shell shell;

Display display;

protected void createSWT(Canvas c) {
display = new Display();
shell = SWT_AWT.new_Shell
(display, c);

shell.setLayout(new FillLayout());

FileViewer app = new FileViewer();

app.open(display,shell);

// Size AWT Panel so that it is big enough to hold the SWT widgets
Point size = shell.computeSize (SWT.DEFAULT, SWT.DEFAULT);
c.setSize
(size.x + 2, size.y + 200);

validate();

// shell.open();
while (!shell.isDisposed())
if (!display.readAndDispatch())
display.sleep();
}

}

en

Recently I asked myself, why swt applications couldn’t run as applet actually? Okay one certain reason is swt is not part of the jre as swing is. However this should not be to dramatic in times of highspeed internet connections, we can download and install it on the fly.

But in addition: SWT needs native libraries and native libraries could not easily loaded by an url classloader like some simple java class files.

But if the applet is signet, then even this isn’t really a problem, and then with little effort you can see …

The example demonstrates a swt demo by IBM: the FileViewer. The class isn’t virtually changed, solely the initialiasation of the shell looks somewaht different.

Further on I added a simple boot loader for swt applets (until now its windows only) und yet a SWT Applet starts.

Warning: This applet indeed is just a demo, but the FileViewer is real: for instance a double click on a word document starts word!!

Note: the applet is signet and gets the permission to install swt after confirmation. To achieve this the applet will dowload two DLLs and store them to the java library path. If this isn’t possible due insufficient rights of the user logged in, the demo wont start and you will get an error message.

Code:

package com.rinke.solutions;

import java.applet.Applet;
import java.awt.Canvas;
import java.awt.FlowLayout;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

import javax.swing.JOptionPane;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.examples.fileviewer.FileViewer;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class SWTApplet extends Applet {

@Override
public void init() {

super.init();
try {
Runtime.getRuntime().loadLibrary("swt-win32-3139.dll");
} catch( Throwable e) {
System.out.println("Load Library failed" );
String path = System.getProperty
("java.library.path");
String
[] paths = path.split(File.pathSeparator);
String targetDir =
null;
for (int i = 0; i < paths.length; i++) {
String t = paths[i];
if( testWriteable(t) ) {
targetDir = t;
break;
}
}
if( targetDir != null ) {
download( "swt-win32-3139.dll", targetDir );
download
( "swt-awt-win32-3139.dll", targetDir );
} else {
JOptionPane.showMessageDialog(this,"Could not download swt dlls, java.library.path not writeable",
"Error Downloading DLLs", JOptionPane.ERROR_MESSAGE);
}
}

resize(640, 400);
final Canvas c = new Canvas();
setLayout
(new FlowLayout());
c.setSize
(640, 400);
add
(c);

new Thread( new Runnable() {

public void run() {
createSWT(c);
}

}).start();

}

private boolean testWriteable(String dir) {
try {
FileOutputStream fos = new FileOutputStream( dir + File.pathSeparator + "test" );
fos.write
(1);
fos.close
();
File f =
new File( dir + File.pathSeparator + "test" );
f.delete
();
return true;
} catch( Exception e){

}
return false;
}

private void download(String filename, String targetDir) {
try {
URL url = new URL( getCodeBase() + filename );
InputStream is = url.openStream
();
File of =
new File(targetDir + "/" + filename);
FileOutputStream o =
new FileOutputStream( of );
System.out.println
("downloading from " + url + "to " + of.getAbsolutePath());
byte[] buffer = new byte[10000];
while( true ) {
int c = is.read(buffer);
if( c == -1 ) break;
o.write
(buffer,0,c);
}
o.close();
is.close
();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

Shell shell;

Display display;

protected void createSWT(Canvas c) {
display = new Display();
shell = SWT_AWT.new_Shell
(display, c);

shell.setLayout(new FillLayout());

FileViewer app = new FileViewer();

app.open(display,shell);

// Size AWT Panel so that it is big enough to hold the SWT widgets
Point size = shell.computeSize (SWT.DEFAULT, SWT.DEFAULT);
c.setSize
(size.x + 2, size.y + 200);

validate();

// shell.open();
while (!shell.isDisposed())
if (!display.readAndDispatch())
display.sleep();

}

}

Dieser Beitrag wurde unter Java veröffentlicht. Setze ein Lesezeichen auf den Permalink.

20 Antworten auf SWT Applikation als Applet

  1. Daniel sagt:

    Cool. Komisch, dass zu diesem Thema so wenig zu finden ist. Über eine Browserdetection könnte man je nach OS die richtige native jar einbinden. Im Firefox 2.0 wir im Beispiel leider nichts angezeigt.

  2. Stefan sagt:

    In der Tat, das bekommt man ohne grosse Problem auch für die anderen Systeme, für die SWT erhältlich ist, zum laufen. Das Firefox 2.0 Problem kann ich leider nicht nachvollziehen, geht bei mir genausogut wie in allen anderen Browsern. Hängt vielleicht vom Sun-Plugin ab …

  3. Daniel sagt:

    Läuft auch im Firefox. War ein Vista Problem. Wie sieht der Bootstrap-Loader aus ist dieser auch für OSX und Linux denkbar?

  4. Stefan sagt:

    Es wird einfach versucht eine native Library zu laden und falls das misslingt, werden alle Pfade des Java.Library.Path auf schreibbarkeit getestet und auf dem ersten Pfad, der funktioniert werden die Libraries runtergeladen und gespeichert.
    Ich werde den Code in den Beitrag einstellen, dann kannst Du mal reinschauen.
    Da SWT für OS X und Linux ebenso verfügbar ist, müßte man nur eine Fallunterscheidung einbauen, die eben nach den für die Zielplattform richtigen Libraries sucht und diese dann ggf. nachlädt, aber das sollte nicht schwierig sein.

  5. Stefan sagt:

    Ich hab mal etwas getestet: das Problem bei Linux ist, dass im Normalfall der Java Library-Path für einen Normal-Benutzer nicht beschreibbar ist. Deshalb nützt die Anpassung des Loaders nicht viel, wenn der Nutzer doch keine Schreibrechte hat. Und wer surft denn schon als root ;-))

  6. Magnus sagt:

    Ich bekomme das Applet leider nicht zum Laufen. Die beiden Bibliotheken werden erfolgreich heruntergeladen, aber in der Zeile display = new Display bekomme ich folgende Exception:

    java.security.AccessControlException: access denied (java.util.PropertyPermission swt.version 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 org.eclipse.swt.internal.Library.loadLibrary(Unknown Source)
    at org.eclipse.swt.internal.Library.loadLibrary(Unknown Source)
    at org.eclipse.swt.internal.C.(Unknown Source)
    … 4 more

    Das signierte Applet habe ich wie folgt eingebunden:

    Jemand eine Idee, wo das Problem liegen könnte?

  7. Stefan Rinke sagt:

    Hi Magnus,

    dieser Fehler tritt immer dann auf, wenn das Applet (trotz Signatur) keine Rechte auf dem System erlangen kann.

    Hast Du die Dialoge, die normalerweise vor der Ausführung eines signierten Applets angezeigt werden, positiv abgeklickt?

    Welches OS und welchen Browser verwendest Du denn?

    Gruß Stefan

  8. Magnus sagt:

    Hi Stefan, ja, die Frage, ob das Applet trotz nicht verifizierter Signatur ausgeführt werden soll, habe ich natürlich bejaht. Getestet hab ich bis jetzt mit Windows XP und Windows Vista, jeweils Firefox 2.0 und Internet Explorer (6?). Überall das gleiche Problem.

    Weiterhin ist mir auch erst jetzt aufgefallen, dass Runtime.getRuntime().loadLibrary(…) immer fehlschlägt (java.lang.UnsatisfiedLinkError: no swt-win32-3346.dll in java.library.path), obwohl beide dlls eigentlich in mindestens einem Ordner des librarypath vorhanden sind (entweder nach Download oder schon zu Programmstart).

    Hier noch meine Verzeichnisstruktur:
    -index.html (startet das Applet)
    -SWTApplet.jar
    -swt.jar (beide Jars im archive Parameter angegeben)
    -swt-win32-3346.dll
    -swt-awt-win32-3346.dll
    Liegt hier vielleicht schon ein Fehler?

  9. Stefan Rinke sagt:

    Damit wir uns nicht missverstehen:

    Der Java-LibraryPath ist nicht das gleiche wie der ClassPath. Die DLL liegen (für den Download) auf der Serverseite zwar direkt bei den JARs. Aber die Java VM muss die DLLs nach dem Download vom lokalen LibraryPath laden können, das ist z.B. C:\Programme\Firefox oder c:\Programme\jdk1.5.0_07\lib.

    Wenn die DLLs dahin nicht herunter geladen werden können oder dort nicht zu finden sind, gibt’s UnsatisfiedLinkage …

    Öffne mal die JavaConsole beim Starten des Applets, da siehst Du etwas Debug-Informationen.

    Gruß Stefan

  10. Magnus sagt:

    Hallo!
    Die gute Nachricht: Es funktioniert! Und zwar OHNE das explizite Laden der DLLs. Mein Problem war, dass ich zwar das Applet signiert hatte, aber nicht swt.jar. Es wäre jetzt natürlich noch schön zu verstehen, was im Hintergrund mit den swt-DLLs passiert. Es scheinen ja welche gefunden zu werden (eclipse? direkt die im Appletverzeichnis?), aber egal ob im librarypath welche sind oder nicht, ich bekomme den UnsatisfiedLinkError. Das Herunterladen der DLLs (in meinem Fall in den Firefoxordner) funktioniert definitiv mit deinem Code. System.load(“absoluter Pfad zu DLLs”) wirft übrigens erstaunlicherweise keinen Fehler… Naja, trotzdem schonmal vielen Dank, zumindest lokal funktioniert es ja schonmal :-) Gruß Magnus

  11. Tino sagt:

    Hi … auch wenn der Thread schon etwas älter ist, frage ich trotzdem mal ;)

    Ich bekomme immernoch trotz signiertem Applet die Fehlermeldung:

    Load Library failed
    java.security.AccessControlException: access denied (java.util.PropertyPermission java.library.path 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 fileupload.init(fileupload.java:57)
    at sun.applet.AppletPanel.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

    Aber auch nur im Browser, in eclipse gehts wunderbar.

    Seltsamer weise bekomme ich auch keine SandboxAbfrage, ob ich zulassen will … weiß du woran das liegen könnte ?

    LG
    Tino

  12. Stefan Rinke sagt:

    Da stimmt was mit der Signatur nicht. Die Fehlermeldung ist typisch und die Tatsache, dass gar nicht nachgefragt wird, genauso.

    Check mal nach, ob mit der Signatur des Applets alles stimmt.

    Gruß Stefan

  13. Tino sagt:

    Hi nochmal,

    DANKE für die fixe Antwort.
    Das Problem war einfach , dass ich die jar im falchen Verzeichniss hatte, aber ich verstehe dann nicht warum er das Applet gestartet hat … seltsam oder ?

    LG
    Tino

  14. Erik sagt:

    Hi,

    ich habe an der gleichen Stelle wie Magnus einen Fehler, aber nicht den
    selben:

    Exception in thread “Thread-5″ java.lang.NoClassDefFoundError:
    org/eclipse/swt/widgets/Display
    at SWTApplet.createSWT(SWTApplet.java:141)
    at SWTApplet$1.run(SWTApplet.java:75)
    at java.lang.Thread.run(Unknown Source)
    (line no.141 bezieht sich auf folgende Codestelle: display = new Display();)

    Bitte um Hilfe… da es im Web anscheinend keine vernünftigen Infos zu
    diesem Thema gibt.

    Thx in advice

    Erik

  15. Stefan Rinke sagt:

    Hi Erik,

    sorry dass ich erst jetzt anworte (war eine Weile im Urlaub), der Stacktrace läßt eigentlich
    nur den Schluss zu, dass die SWT-Libs nicht verfügbar sind. Hast Du sichergestellt, dass
    alle JARs auf dem Server installiert wurden? Hast Du mal das Access-Log des Servers
    gecheckt? Oder beziehst Du Dich auf meine Installation?

    Schalte in jedem Fall mal die Java-Console an, damit alle Meldungen, die dort auftauchen,
    auch sichtbar werden.

    Gruß Stefan

  16. Erik sagt:

    Hallo Stefan,

    danke für die Antwort. Ich habe es in der Zwischenzeit geschafft unsere SWT Applikation als Applet laufen zu lassen.

    Der Grund, warum wir eine SWT App als Applet laufen lassen sollen, kommt von einer Anforderung unseres Kunden. Nun technisch ist es möglich, aber was ich mich die ganze Zeit Frage ist, ist es so ungewöhnlich ein SWT Applet zu erstellen?

    Es war für mich sehr schwer im Web Informationen diesbezüglich zu finden, weder unter Eclipse.org noch via Google. Dein Blog war eher die einzige, wirkliche Informationsquelle (und btw eine wirklich Sehr Gute!!!).

    Das Applet, welches Du in deinem Sample erstellt hast, sollte nur in einem Windows Environment lauffähig sein. Hast Du Erfahrungswerte, was man berücksichtigen muss, um das Applet auf weiteren OS lauffähig zu machen?

    Genügt es einfach im Lib Directory das entsprechende JAR für das OS zu haben und im Applet die entsprechende .dll, .so, usw. zu laden, nachdem man die OS Version abgefragt hat?

    Danke und schöne Grüsse
    Erik

  17. Stefan Rinke sagt:

    Hi Erik,

    schön dass das nun läuft. In der Tat findet man im Netz wenig über das Thema, was für mich ja gerade der Grund war mich einmal damit zu befassen.

    Es ist wohl in so fern ungewöhnlich SWT in einem Applet zu betreiben, weil mit dem JRE eben nicht alle Voraussetzungen erfüllt sind (die Nativ-SWT-Libs fehlen), und man daher nicht davon ausgehen kann, dass es überhaupt läuft.

    > Genügt es einfach im Lib Directory das entsprechende JAR für das OS zu haben
    > und im Applet die entsprechende .dll, .so, usw. zu laden, nachdem man die OS
    > Version abgefragt hat?

    Im Prinzip ja. Allerdings haben meine Versuche unter Linux beispielsweise gezeigt, dass ein normaler Benutzer dort – im Gegensatz zum typischen Windows-Benutzer – gar nicht die Rechte hat, auf den Java-Library-Path zu schreiben. Damit fällt das “On-The-Fly” Installieren der SWT-Native-Library aus. Man müßte also für das Deployment was anderes vorsehen. z.B. Java Web Start siehe: http://www.eclipse.org/swt/jws/

    Aber das ist ja nicht ganz dasselbe :-)

    Gruß Stefan

  18. Anon sagt:

    Ein Problem mit diese Methode ist dass es für mich als Benutzer keinen Weg gibt um zu bestätigen dass das Applet die Rechte nur nützen wird um SWT zu nützen.
    Also denke ich dass ein kluger Benutzer das Dialogfenster wegklicken wird und dann irgendwo anders hingeht.

  19. Tobias sagt:

    Was bringts, die native libraries nachzuladen, wenn die SWT-JARs auch plattformabhängig sind? Oder hab ich hier nur was nicht gechecket? Oder liegts daran, dass das Thema 4 Jahre alt ist?

    Gruß Tobias

  20. Stefan Rinke sagt:

    Die swt.jars für unterschiedliche Plattformen vorzuhalten, ist nicht das Problem. Die kann man ja auf dem Server deployen. Das Problem worum es hier ging ist eher: wie sorge ich auf dem Client dafür, dass die JVM auch SWT kann. Wie also den Nativcodeanteil “nachrüsten”.

    Gruß Stefan

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>