Ο κώδικας που ακολουθεί είναι ένα πολύ απλό Java applet που εμφανίζει το μήνυμα "Hello World.".
import java.applet.Applet; // importing the Applet objectΗ εκτέλεση του applet απαιτεί τη ύπαρξη μιας ιστοσελίδας μέσω της οποίας καλείται η κλάση του applet. Ο κώδικας μια ελάχιστης τέτοιας ιστοσελίδας, έστω με όνομα
import java.awt.*; // importing the ‘abstract window toolkit’
public class HelloWorldApplet extends Applet
{
public void paint(Graphics g)
{
g.drawString("Hello World.",10,10);
}
}Hello.html
, φαίνεται παρακάτω:
<HTML>
<BODY>
<APPLET CODE="HelloWorldApplet.class" WIDTH=150 HEIGHT=50>
</APPLET>
</BODY>
</HTML>
Τo applet εκτελείται με το άνοιγμα της ιστοσελίδας μέσω ενός περιηγητή (browser). H διαχείριση του applet γίνεται από το Java Plug-in του περιηγητή, το οποίο θα πρέπει να είναι συμβατό με το bytecode του applet. Εναλλακτικά η ιστοσελίδα μπορεί να φορτωθεί μέσω της εφαρμογήςappletviewer.
appletviewer Hello.html
To Java Plug-in του περιηγητή δημιουργεί ένα νήμα για κάθε applet που περιέχει μια ιστοσελίδα. Τα νήματα αυτά εκτελούνται σε ένα στιγμιότυπο του Java Runtime Environment (JRE) που είναι ενσωματωμένο στο περιηγητή.
Τα applets εκτελούνται στο ίδιο στιγμιότυπο JRE αν:
Διαφορετικά τα applets εκελούνται σε ξεχωριστά στιγμιότυπα του JRE. Νήματα που ανήκουν στο ίδιο Applet εκτελούνται στο ίδιο JRE.
- Δεν υπάρχει πρόβλημα με την εκτέλεση στη συγκεκριμένη έκδοση του JRE (έκδοση ίδια ή μεταγενέστερη).
- Δεν υπάρχει πρόβημα με πιθανές παραμέτρους εκτέλεσης του applet.
Στο διάγραμμα που ακολουθεί τα applets Α, Β εκτελούνται στο ίδιο JRE ενώ το applet C σε διαφορετικό JRE.
To applet που παρουσιάστηκε επεκτείνει τη κλάση
java.applet.Applet
. Παρακάτω παρουσιάζεται μια εναλλακτική έκδοση ενός applet που χρησιμοποιεί τη κλάσηjava.applet.JApplet
που επιτρέπει τη χρήση συστατικών GUI Swing. Τα applets κληρονομούν σημαντική λειτουργικότητα απο τις κλάσειςApplet
ήJApplet
, όπως δυνατότητες επικοινωνίας με το περιηγητή και το χειρισμό της γραφικής διεπαφής.'Ενα άλλο ενδιαφέρον χαρακτηριστικό είναι η χρήση νημάτων που θα συζητηθεί παρακάτω.
Ακολουθεί εναλλακτικός κώδικας:
Σημείωση: αν το applet δε λειτουργήσει σωστά, ελέγξτε το διερμηνέα JavaScript του περιηγητή σας.
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
public class HelloWorldJApplet extends JApplet {
//Called when this applet is loaded into the browser.
public void init() {
//Execute a job on the event-dispatching thread; creating this applet's GUI.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
JLabel lbl = new JLabel("Hello World");
add(lbl);
}
});
} catch (Exception e) {
System.err.println("createGUI didn't complete successfully");
}
}
}
Οι κλάσεις
Applet, JApplet
, παρέχουν ένα πλαίσιο εκτέλεσης ενός applet με βάση ορισμένα ορόσημα. Τα ορόσημα είναι σημαντικά γεγονότα στο κύκλο ζωής ενός applet.
O κύκλος ζωής ενός applet έχει τα εξής ορόσημα:
- Αρχικοποίηση.
- Εκτέλεση.
- Αναστολή.
- Καθαρισμός.
init
MethodΗ μέθοδος
init
χρησιμοποιείται για την αρχικοποίηση του applet. Είναι η πρώτη μέθοδος που καλείται, γι' αυτό ο κώδικας πρέπει να είναι σύντομος ώστε το applet να φορτώνεται σύντομα.
start
MethodΗ μέθοδος
start
ξεκινά την εκτέλεση του applet.
stop
MethodΗ μέθοδος
stop
αναστέλει την εκτέλεση του applet.
destroy
MethodΗ μέθοδος
destroy
απελευθερώνει πόρους που πιθανώς δεσμεύει το applet.
Ακολουθεί ο κώδικας του applet
Simple
. To applet αυτό εμφανίζει ένα μήνυμα όταν συναντά ένα ορόσημο του κύκλου ζωής του. Η εκτέλεση του applet εξηγείται παρακάτω.
import java.applet.Applet;
import java.awt.Graphics;
//No need to extend JApplet, since we don't add any components;
//we just paint.
public class Simple extends Applet {
StringBuffer buffer;
public void init() {
buffer = new StringBuffer();
addItem("initializing... ");
}
public void start() {
addItem("starting... ");
}
public void stop() {
addItem("stopping... ");
}
public void destroy() {
addItem("preparing for unloading...");
}
private void addItem(String newWord) {
System.out.println(newWord);
buffer.append(newWord);
repaint();
}
public void paint(Graphics g) {
//Draw a Rectangle around the applet's display area.
g.drawRect(0, 0,
getWidth() - 1,
getHeight() - 1);
//Draw the current string inside the rectangle.
g.drawString(buffer.toString(), 5, 15);
}
}
Φόρτωση
Κατά τη φόρτωση του applet γίνεται η αρχικοποίηση και εμφανίζεται το μήνυμα "initializing... starting...". Κατά το φόρτωση:
- Δημιουργείται ένα στιγμιότυπο της βασικής κλάσης του applet (
Applet, JApplet
υποκλάση).- Το applet αρχικοποιείται.
- Το applet αρχίζει να εκτελείται.
Αποχώρηση και Επιστροφή
Όταν ο χρήστης αποχωρεί από την ιστοσελίδα του applet, πχ για να δεί μια άλλη σελίδα, ο περιηγητής σταματά και καταστρέφει το applet. Η κατάσταση του applet δεν διατηρείται. Όταν ο χρήστης επιστρέφει στην ιστοσελίδα, το applet φορτώνεται από την αρχή.
Επαναφόρτωση
Αν γίνει επαναφόρτωση (ή refresh) το τρέχον στιγμιότυπο του applet καταστρέφεται και δημιουργείται νέο στιγμιότυπο.
Κλείσιμο
Όταν ο χρήστης εγκαταλείπει το περιηγητή το applet σταματά και απελευθερώνει τους πόρους.
Η κλάση ThreadExample.java
επεκτείνει (extends
) τη κλάση Applet
αλλά
επιπλέον υλοποιεί (implements
) της διεπιφάνεια Runnable
.
Για να την εκτελέσετε μπορείτε να χρησιμιποιείσετε το αρχείο Thread
T
est.html
.
Κατά την εκτέλεση της μεθόδου init
του applet
δημιουργείται ένα νήμα που κληρονομεί το κώδικα του applet.
public class ThreadExample extends Applet implements Runnable {
// Define your thread.
Thread clockThread;
boolean running = true;
...
public void init() {
...
// Create the thread.
clockThread= new Thread(this);
// and let it start running
clockThread.start();
}
...
Έτσι, με τη κλήση της μεθόδου sta
rt
to νήμα του applet εκτελεί το κώδικα της μεθόδου run
που
ακολουθεί.
public void run() {
// loop until told to stop
while (running) {
...
// Now the reason for threads
try {
// Wait 500 milliseconds before continuing
clockThread.sleep(500);
}
catch (InterruptedException e) {
System.out.println(e);
}
// he has wait and will now restart his actions.
}
To νήμα αναστέλει την εκτέλεσή του για 500 msec. Η καθυστέρηση αυτή είναι σημαντική όταν υπάρχουν γραφικά που απαιτούν επανασχεδίαση, ή άλλα νήματα του applet που περιμένουν να εκτελεστούν.
Η μεταβλητή running
επιτρέπει
το τερματισμό του νήματος κατά το κλείσιμο του applet με κλήση της
μεθόδου destroy
.
public void destroy() {
// will cause thread to stop looping
running = false;
// destroy it.
clockThread = null;
}
Έστω οτι η κλάση AppletClient.java
βρίσκεται μεταγλωττισμένη (δηλαδή ως AppletC
lient.class
)
σε ένα κατάλογο ενός διακομιστή ιστού, έστω
http://Some.Server.com
, μαζί με τη κατάλληλη HTML σελίδα NetHello.html
.
Μέσω αυτής της σελίδας μεταφρορτώνεται και
εκτελείται η κλάση AppletClient.class
από το σύστημα του διακομιστή στον υπολογιστή του πελάτη, όπου και
εκτελείται μέσα στο περιβάλλον Java Plug-in του περιηγητή. Αφού
ξεκινήσει η εφαρμογή μέσω του applet, συμπεριφέρεται πλέον ως κανονικός
πελάτης σεπρογραμματισμό υποδοχών.
Το αρχείο κλάσης του πελάτη πρέπει να βρίσκεται στη διαδρομή που ορίζει η σελίδα html. Τα αρχεία που σχετίζονται με το διακομιστή μπορούν να τοποθετηθούν ανεξάρτητα και να εκτελεστούν όπως σε οποιαδήποτε εφαρμογή πελάτη διακομιστή. Η εφαρμογή του διακομιστή πρέπει να εκτελεστεί πριν την εφαρμογή του πελάτη applet.
Από ένα περιηγητή ανοίγουμε την ιστοσελίδα έστω
http://Some.Server.com
/NetHello.html
O κώδικας του πελάτη φαίνεται ο παρακάτω:
import java.io.*;
import java.net.*;
import java.awt.*;
import java.applet.*;
public class AppletClient extends Applet {
public void init() {
Label label=new Label(" ");
int port=5000;
add(label);
try {
Socket socket=new Socket(this.getCodeBase().getHost(), port); // connect to server
BufferedReader bufferedreader=new BufferedReader(new // set up in and out streams
InputStreamReader(socket.getInputStream()));
PrintWriter printwriter=new PrintWriter(socket.getOutputStream(),true);
printwriter.println("java"); // send "java" message to server
String string=bufferedreader.readLine(); // receive message from server
label.setText(string); // should be "javaworld"
}
catch(Exception error){
label.setText(error.getMessage()); //cannot access host:port
}
}
}
Στό κώδικα του πελάτη παρατηρούμε οτι
χρησιμοποιείται μόνο η μέθοδος init
, για λόγους
απλότητας. Έτσι, για παράδειγμα τα streams και το socket δεν κλείνουν
κανονικά στο τέλος της επικοινωνίας. Επίσης η 'κύρια' λειτουργία δεν
έχει διαχωριστεί από την αρχικοπόιηση.
Tο πιο ενδιαφέρον σημείο του κώδικα είναι η
χρήση της μεθόδου
getCodeBase().get
Host()
στο δημιουργούμενο αντικείμενο socket. Με αυτό το τρόπο δεν απαιτείται
πρότερη γνώση της ακριβούς ΙP διεύθυνσης του διακομιστή, αφού αυτή
αναγνωρίζεται δυναμικά κατά τη μεταφορά του applet από το διακομιστή
ιστού στο JRE του περιηγητή.
Η εφαρμογή του διακομιστή δινεται στο αρχείο
Server.java
και
φαίνεται παρακάτω
import java.net.*;
import java.io.*;
public class Server{
public static void main(String args[]) {
int port=5000;
try {
ServerSocket serversocket=new ServerSocket(port);
Socket socket=serversocket.accept(); // wait for client connection
BufferedReader bufferedreader=new BufferedReader(new
InputStreamReader(socket.getInputStream()));
PrintWriter printwriter=new PrintWriter(socket.getOutputStream(),true); //set up in and out streams
String string1=bufferedreader.readLine(); // receive message from client
String string2=string1+"world";
printwriter.println(string2); // send message back to client
}
catch(IOException error) {}
}
}
Όπως και στο πελάτη ο κώδικας είναι πολύ απλός, για παράδειγμα δεν περιλαμβάνει κλείσιμο των streams και sockets.
Η κλάση QuoteClientApplet.java
επιτρέπει την λήψη και την εμφάνιση quotations απο ένα διακομιστή.
Οι κλάσεις QuoteServer.java
και QuoteServerThread.java
συνθέτουν την εφαρμογή του διακομιστή.
Το αρχείο one-liners.txt
που περιέχει
quotations.
quoteApplet.html
είναι αυτή που θα φορτωθεί στο περιηγητή για την εκτέλεση του applet. Εμφανίζεται τοhttp://JohnDoeMachine:8080/quoteApplet/quoteApplet.html
QuoteClientApplet
.
QuoteServer
Sample OutputTόσο JRE, όσο και το Java Plug-in των περιηγητών, χρησιμοποιούν το
Διαχειριστή Ασφαλείας (Security Manager) της Java ώστε να αποτρέπουν
κακόβουλο κώδικα να προσβάλει το σύστημα μέσω της εκτέλεσης ενός applet.
Ένα applet μπορεί να έχει πρόσβαση σε πόρο του συστήματος, μόνο αν
ο Διαχειριστής Ασφαλείας διαπιστώσει οτι το συγκεκριμένο applet έχει
άδεια πρόσβασης στο συγκεκριμένο πόρο. Αυτή η άδεια καθορίζεται μέσω
ενός αρχείου πολιτικής ασφάλειας (policy file).
Η πολιτική ασφάλειας γίνεται αντιληπτή μόνο όταν το applet (ή γενικότερα κάποιος φιλοξενούμενος κώδικας) που εκετελείται στο τοπικό JRE ή Java Plug-in προσπαθήσει να προσπελάσει πόρους εκτός του περιβάλλοντος αυτού. Τέτοιοι πόροι είναι, για παράδειγμα, το σύστημα αρχείων, η διεπιφάνεια δικτύου, εντολές συστήματος κλπ. Αν όμως ο κώδικας δρά καθαρά εντός του περιβάλλοντος εκτέλεσης (όπως πχ στο αρχικό παράδειγμα applet) τότε η πολιτική ασφάλειας δεν είναι απαραίτητη.
Το πρόβλημα αυτό θα έγινε ήδη αντιληπτό αν προσπαθήσατε να εκτελέσετε
εφαρμογή πελάτη διακομιστή με appletviewer. Στη περίπτωση χρήσης
περιηγητή το Java Plug-in χρησιμοποιεί το Διαχειριστή Ασφαλείας του
περιηγητή, o όποίος φέρεται διαφορετικά. Συνήθως παράγει ένα πλαίσιο
διαλόγου (Security Warning) με επιλογές Yes / No. Όμως, στη περίπτωση
του appletviewer, αν ο Διαχειριστής Ασφάλειας δεν είναι σωστά
ρυθμισμένος, η εφαρμογή μας καταρρέει.
Το applet
WriteFile.java
που ακολουθεί προσπαθεί να
δημιουργήσει και να γράφει σε ένα αρχείο με όνομα writetest
στο τρέχοντα κατάλογο. Για να λειτουργήσει πρέπει να υπάρχει άδεια
εγγραφής στο σύστημα αρχείων (στον τρέχοντα κατάλογο συγκεκριμένα) η
οποία θα χορηγηθεί μέσω του αντίστοιχου αρχείου πολιτικής ασφάλειας.
import java.awt.*;
import java.io.*;
import java.lang.*;
import java.applet.*;
public class WriteFile extends Applet {
String myFile = "writetest";
File f = new File(myFile);
DataOutputStream dos;
public void init() {
String osname = System.getProperty("os.name");
}
public void paint(Graphics g) {
try {
dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(myFile),128));
dos.writeChars("Cats can hypnotize you when you least expect it\n");
dos.flush();
g.drawString("Successfully wrote to the file named " + myFile + " -- go take a look at it!", 10, 10);
}
catch (SecurityException e) {
g.drawString("writeFile: caught security exception: " + e, 10, 10);
}
catch (IOException ioe) {
g.drawString("writeFile: caught i/o exception", 10, 10);
}
}
}
Παρακάτω δίνεται ένα html αρχείο που καλεί το applet:
<html>
<boby>
<hr>
Here's an applet that tries to write to the file named "writetest".
<p>
<applet code=WriteFile.class width=500 height=150 alt="WritingFile applet">
</applet>
<p>
Here's the <a href=WriteFile.java>source</a>.
<p>
This applet won't work unless you've configured your policy to allow it to write such a file; if not, you will see an AccessControlException.
If you've configured your system to allow applets from this applets' location to write the specified file, check your current directory!
You'll find a file there named "writetest".
</body>
</html>
appletviewer WriteFile.htmlΠρέπει να δείτε ένα μήνυμα σχετικά με μια εξαίρεση ασφαλείας, όπως φαίνεται παρακάτω. Το applet δεν έχει δικαίωμα να γράψει (write permissions) στο τρέχοντα κατάλογο.
policy.url=file:${java.home}/lib/security/java.policy
${java.home}
αντικαθίσταται από το σύστημα
εκτέλεσης με το pathname οπου είναι εγκατεστημένο το
JRE.
Αν δεν γνωρίζετε το pathname μπορείτε να το βρείτε με τη παρακάτω
μέθοδοΤο policy file του JRE είναι το προεπιλεγμένο αρχείο που διαβάζει ο Διαχειριστής Ασφάλειας, αν ο χρήστης δεν ορίσει κάτι διαφορετικό.$ which java
/usr/bin/java
$ ls -l /usr/bin/java
... /usr/bin/java -> /etc/alternatives/java
$ ls -l /etc/alternatives/java
... /etc/alternatives/java -> /usr/lib/jvm/java-7-oracle/jre/bin/java
$ cat /usr/lib/jvm/java-7-oracle/jre/lib/security/java.policy
appletviewer -J-Djava.security.policy=policy.url WriteFile.html
policy.url
αντιστοχεί σε οποιοδήποτε έγκυρο
pathname (URLname). Aν για παράδειγμα το policy file της εκτέλεσης
βρίσκεται στον ίδιο κατάλογο με το ΗΤΜL αρχείο WriteFile.html
και ονομάζεται mypolicy
τότε η σύνταξη της εντολής
εκτέλεσης θα είναι η εξήςappletviewer -J-Djava.security.policy=mypolicy WriteFile.htmlTο pathname θα μπορούσε να έχει τη μορφή
file:/home/myhome/mypolicy
ή http://Some.Server.com/Policies/mypolicy
policy.url=file:${user.home}/.java.policyόπου η τιμή
${user.home}
αντικαθίσταται από το σύστημα
εκτέλεσης με το pathname του οικείου καταλόγου του χρήστη. Αν δε
γνωρίζετε το pathname μπορείτε να το βρείτε με την εντολή$echo $HOME
To policy file χρήστη μας επιτρέπει να συλλέξουμε σε ένα σημείο όλες
τις επιυθμητές πολιτικές ασφάλειας που μας αφορούν ώστε η κλήση του
Διαχειριστή Ασφάλειας να είναι ευκολότερη.mypolicy
βρίσκεται στον ίδιο κατάλογο με το WriteFile.html
και
το WriteFile
applet, η εκτέλεση της εντολής
appletviewer -J-Djava.security.policy=mypolicy WriteFile.html
θα επιτρέψει τη δημιουργία και εγγραφή στο αρχείο writetest
,
σύμφωνα με το παρακάτω σχήμα.
grant [signedBy <SignerNames>],Tα πεδία signedBy και principal δεν εξετάζονται εδώ. Για ανάλυση και παραδείγματα δείτε στο σύνδεσμο
[codebase <Codebase>],
[principal prinicipal_class_name <PrincipalName>] { permission <Permission>; permission <Permission>; permission <Permission>; };
<CodeBase>: A URL. For example, "file:${java.home}/lib/tools.jar", "http://Some.Server.com/applets/" When [codebase <Codebase>] is not specified, listed permissions are applied to everything. If URL ends with a JAR file name, only the classes in the JAR file belong to the codebase. If URL ends with "/", only the class files in the specified directory belong to the codebase. If URL ends with "*", all JAR and class files in the specified directory belong to the codebase. If URL ends with "-", all JAR and class files in the specified directory and its subdirectories belong to the codebase.
<Permissions>: Consists of Permission Type : class name of the permission Target Name : name specifying the target Actions : actions allowed on target For example, the following syntax grants read and write permissions on file /tmp/xxx java.io.FilePermission "/tmp/xxx", "read, write"
http://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
. WriteFile
, ένα απλό
policy file θα έχει τη μορφήgrant {που σημαίνει οτι ένα applet με οποιοδήποτε όνομα και προέλευση έχει δικαιώματα εγγραφής στο αρχείο
permission java.io.FilePermission "writetest", "write";
};
writetest
στον τρέχοντα
κατάλογο. grant { permission java.net.SocketPermission "Some.Server.com:5000", "connect, accept, resolve"; };που σημαίνει αποδοχή σύδεσης και επικοινωνίας μέσω υποδοχών με τη θύρα 5000 του συγκεκριμένου διακομιστή.
grant { permission java.security.AllPermission; };μας επιτρέπει να εκτελέσουμε οποιοδήποτε applet ή κλάση Java.
http://download.java.net/jdk8/docs/technotes/guides/security/permissions.html
περιγράφει τη σύνταξη και τις ιδιότητες όλων των security permissions
στη Java.http://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
.policytool
To Policy Tool κατ' αρχήν προσπαθεί να αντλήσει πληροφορίες από το
προεπιλεγμένο user policy file. Το user policy file, εάν αυτό
υπάρχει, πρέπει να ονομάζεται .java.policy
και να
βρίσκεται στον user home directory.
Τη πρώτη φορά που θα χρησιμοποιήσετε το Policy Tool θα δείτε ένα άδειο παράθυρο όπως αυτό.
Αν ορίσετε τιμές στο CodeBase και στο SignedBy, τότε παρέχετε άδειες μόνο σε κώδικα από συγκεκριμένη πηγή και με συγκεκριμένη υπογραφή.
Η απλούστερη επιλογή είναι να αφήσουμε τις δύο γραμμές άδειες.Επιλέγουμε το κουμπί Add Pemission και εμφανίζεται το παρακάτω πλαίσιο διαλόγου.
java.io.FilePermission
.
:
writetest
Επιλέγουμε OK. Η νέα πολιτική ασφάλειας έχει καταγραφεί. Για να
τη σώσουμε σε ένα αρχείο policy file πηγαίνουμε στο Save As από
το File menu. Μετακινούμαστε πχ στο κατάλογο όπου βρίσκεται η
html σελίδα που θα φορτώσει το applet και σώζουμε το policy file ως mypolicy
.
Σε επόμενη χρήση του Policy Tool μπορούμε να φορτώσουμε,
επιθεωρήσουμε ή διιορθώσουμε το policy file μας. Εάν θελήσουμε να
εμφανίσουμε το policy file με την εντολή cat θα δούμε το εξής αρχείο:
grant {
permission java.io.FilePermission "writetest", "write";
};