Programmazione avanzata in Java - IsIB

Programmazione avanzata in Java
Paolo Bison
Technical Report 13/00, LADSEB-CNR
Padova, Italy, Novembre 2000.
LADSEB-CNR
Corso Stati Uniti 4
I-35127 Padova, Italy
e-mail:[email protected]
tel: 049 8295765
fax: 049 8295763
PREFAZIONE
In questo rapporto vengono descritte le funzionalitá piú avanzate del linguaggio Java illustrandole con esempi applicativi. Verranno presentati la programmazione concorrente basata sui threads, l’attivazione di programmi scritti in altri
linguaggi, l’interfaccia grafica basata sull’ Abstract Window System ed il metodo Remote Method Invocation per la
comunicazione tra diverse maccine virtuali java.
ABSTRACT
This report describes the advanced features of the Java language, such as the concurrent programming based on
threads, the native interface used to call procedures written in other programming languages, the Abstract Window
System used to build graphical interfaces and the Remote Method Invocation for exchanging data between java virtual
machines.
INDICE
1 Abstract Window Toolkit (AWT)
1.1 Interfaccia grafica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Uscita su stampante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Un semplice editore di testi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
2
6
8
2 Multiprogrammazione.
2.1 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Loops temporizzato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
13
14
3 Remote Method Invocation
3.1 Client Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Robot Name Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Information Delivery Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
15
21
30
4 Autoreferenzialit á
4.1 Matrici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Calcolo tempo di esecuzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
35
40
5 Programmi nativi
5.1 Stampa messaggio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Somma ed inversione di un array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
43
45
Capitolo 1
Abstract Window Toolkit (AWT)
In questo capitolo sono mostrati alcuni esempi di programmi che utilizzano le funzionalitá del Abstract Window
Toolkit (AWT) presente nel linguaggio Java per la realizzazione di interfacce grafiche.
AWT é un sistema basato su eventi e la tabella seguente illustra a quali classi di eventi rispondiono i differenti
elmenti grafici.
action
Button
Canvas
Checkbox
CheckboxMenuItem
Choice
Component
Container
Dialog
Frame
Label
List
MenuItem
Panel
Scrollbar
ScrollPane
TextArea
TextComponent
TextField
Window
adjustment
X
component
container
focus
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
*
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
item
X
X
X
X
key
mouse
mouse motion
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
text
window
X
X
X
X
X
X
Listener Interface
ActionListener
AdjustmentListener
ComponentListener
Adapter Class
none
none
ComponentAdapter
ContainerListener
ContainerAdapter
FocusListener
FocusAdapter
ItemListener
KeyListener
none
KeyAdapter
MouseListener
MouseAdapter
MouseMotionListener
MouseMotionAdapter
TextListener
WindowListener
none
WindowAdapter
Methods
actionPerformed
adjustmentValueChanged
componentHidden
componentMoved
componentResized
componentShown
componentAdded
componentRemoved
focusGained
focusLost
itemStateChanged
keyPressed
keyReleased
keyTyped
mouseClicked
mouseEntered
mouseExited
mousePressed
mouseReleased
mouseDragged
mouseMoved
textValueChanged
windowActivated
windowClosed
windowClosing
windowDeactivated
windowDeiconified
windowIconified
windowOpened
1.1 Interfaccia grafica
Questo programma mostra i vari elementi grafici che si possono usare in un programma java.
Listato 1.1 - GUIWindow.java: elementi del AWT
import java.awt. *;
import java.awt.event. *;
public class AWTtest extends Frame
{
final String FDCMD = "File dialog...";
final String QUITCMD = "Quit";
public AWTtest ()
{
/* creazione panel superiore */
Panel bottomPanel = new Panel ();
Panel centerPanel = new Panel ();
setLayout (new BorderLayout ());
MenuBar mb = new MenuBar ();
/* creazione di un menu */
Menu m = new Menu ("Primo");
MenuItem mi = new MenuItem ("Menu A");
mi.addActionListener (new ActionEventManager ());
m.add (mi);
CheckboxMenuItem mcbi = new CheckboxMenuItem ("Menu B");
mcbi.addItemListener (new ItemManager ());
m.add (mcbi);
mi = new MenuItem ("Menu C");
mi.addActionListener (new ActionEventManager ());
m.add (mi);
m.add (new MenuItem ("-")); /* separatore */
mi = new MenuItem (FDCMD);
mi.addActionListener (new FDManager ());
m.add (mi);
m.add (new MenuItem ("-")); /* separatore */
mi = new MenuItem (QUITCMD);
mi.addActionListener (new ActionEventManager ());
m.add (mi);
mb.add (m);
/* aggiungi menu alla barra dei menu */
/* creazione di un altro menu */
m = new Menu ("Secondo");
mi = new MenuItem ("Menu altro A");
mi.addActionListener (new ActionEventManager ());
m.add (mi);
mi = new MenuItem ("Menu altro B");
mi.addActionListener (new ActionEventManager ());
m.add (mi);
mb.add (m);
/* aggiungi menu alla barra dei menu */
setMenuBar (mb);
/* assegna la barra dei menu alla finestra */
//Add small things at the bottom of the window.
bottomPanel.add (new TextField ("TextField"));
bottomPanel.add (new Button ("Button"));
bottomPanel.add (new Checkbox ("Checkbox"));
Choice c = new Choice ();
c.addItem ("Scegli 0");
c.addItem ("Scegli 1");
c.addItem ("Scegli 2");
bottomPanel.add (c);
add ("South", bottomPanel);
//Add big things to the center area of the window.
centerPanel.setLayout (new GridLayout (1, 2));
//Put a label and a text area in the right column.
Panel p = new Panel ();
p.setLayout (new BorderLayout ());
p.add ("North", new Label ("Label", Label.CENTER));
p.add ("Center", new TextArea ("TextArea", 5, 20));
centerPanel.add (p);
//Put a canvas in the left column.
centerPanel.add (new testCanvas ());
add ("Center", centerPanel);
//Put a list on the right side of the window.
List l = new List (3, false);
for (int i = 1; i <= 10; i++)
{
l.addItem ("List item " + i);
}
add ("East", l);
addWindowListener (new WindowEventManager ());
}
/**
inner class per la gestione degli eventi
generati
dalla finestra
*/
class WindowEventManager implements WindowListener
{
public void windowClosing (WindowEvent e)
{
displayMessage ("Window closing", e);
System.exit (0);
}
public void windowClosed (WindowEvent e)
{
displayMessage ("Window closed", e);
}
public void windowOpened (WindowEvent e)
{
displayMessage ("Window opened", e);
}
public void windowIconified (WindowEvent e)
{
displayMessage ("Window iconified", e);
}
public void windowDeiconified (WindowEvent e)
{
displayMessage ("Window deiconified", e);
}
public void windowActivated (WindowEvent e)
{
displayMessage ("Window activated", e);
}
public void windowDeactivated (WindowEvent e)
{
displayMessage ("Window deactivated", e);
}
void displayMessage (String prefix, WindowEvent e)
{
System.out.println (prefix
+ ": "
+ e.getWindow ()
);
}
}
/**
inner class per la gestione degli action event
che possono essere genarati da un button, un menuitem, un textfield o un listitem
*/
class ActionEventManager implements ActionListener
{
public void actionPerformed (ActionEvent e)
{
String cmd = e.getActionCommand ();
System.out.println ("Perfom action: "
+ cmd);
if (cmd.compareTo (QUITCMD) == 0)
System.exit (0);
}
}
/**
inner class per la gestione del file dialog
che possono essere genarati da un button, un menuitem, un textfield o un listitem
*/
class FDManager implements ActionListener
{
public void actionPerformed (ActionEvent e)
{
FileDialog fd = new FileDialog (AWTtest.this);
fd.show ();
}
}
/**
inner class per la gestione del cambiamento di stato
degli elementi di tipo check, choice o list */
class ItemManager implements ItemListener
{
public void itemStateChanged (ItemEvent e)
{
System.out.println ("Change state: "
+ e.getItem ());
}
}
public static void main (String args[])
{
AWTtest window = new AWTtest ();
window.setTitle ("AWT test");
window.pack ();
window.show ();
}
/**
testCanvas
inner classe per definire qualcosa da disegnare
*/
class testCanvas extends Canvas
{
public void paint (Graphics g)
{
int w = getSize ().width;
int h = getSize ().height;
g.setColor (Color.red);
g.drawRect (0, 0, w - 1, h - 1);
g.drawString ("Canvas", (w - g.getFontMetrics ().stringWidth ("Canvas")) / 2,
10);
g.setFont (new Font ("Helvetica", Font.PLAIN, 8));
g.drawLine (10, 10, 100, 100);
g.fillRect (9, 9, 3, 3);
g.drawString ("(10,10)", 13, 10);
g.fillRect (49, 49, 3, 3);
g.drawString ("(50,50)", 53, 50);
g.fillRect (99, 99, 3, 3);
g.drawString ("(100,100)", 103, 100);
}
/* se non si specificano i metodi seguenti la canvas qualche
volta non viene disegnata */
public Dimension getMinimumSize ()
{
return new Dimension (200, 200);
}
public Dimension getPreferredSize ()
{
return getMinimumSize ();
}
}
}
1.2 Uscita su stampante
Esempio di come si possano utilizzare le funzionalitá di AWT per realizzare una uscita grafica su stampante.
Listato 1.2 - PrintTest.java: esempio di stampa
import java.awt.*;
public class PrintTest extends Frame
{
Frame printframe;
public PrintTest (String title)
{
super (title);
printframe = new Frame ();
printframe.setSize (200, 200);
printframe.pack ();
}
/**
event handler stile 1.0
*/
public boolean handleEvent(Event event) {
if (event.id == Event.WINDOW_DESTROY)
System.exit(0);
return super.handleEvent(event);
}
public static void main (String[]args)
{
Graphics g;
try
{
PrintTest mainframe = new PrintTest ("Print Test");
mainframe.setSize (200, 200);
mainframe.show ();
Color bg = mainframe.getForeground ();
String[] st = Toolkit.getDefaultToolkit ().getFontList ();
for (int j = 0; j < st.length; j++)
System.out.println (st[j]);
Font font = new Font ("Helvetica", Font.PLAIN, 12);
PrintJob pj =
Toolkit.getDefaultToolkit ().getPrintJob (mainframe.printframe, "Print Test", null);
if (pj != null)
{
System.out.println ("PageDimension " + pj.getPageDimension ());
System.out.println ("PageResolution " + pj.getPageResolution ());
// stampa due pagine
for (int i = 1; i < 3; i++)
{
g = pj.getGraphics ();
// assegna colore e font
// se non sono presenti alcune primitive grafiche generano
// una eccezione NullPointer
g.setColor (bg);
g.setFont (font);
g.drawString ("Pagina " + i, 50, 50);
g.drawString ("Pagina " + i, 50, 75);
if (i == 1)
{
g.drawRect (100, 100, 100, 100); // se non e’ presente viene stampata un linea extr
g.drawOval (250, 250, 100, 100);
}
else
{
g.drawRect (200, 200, 100, 100);
}
mainframe.printframe.printComponents (g);
g.dispose ();
// send to the printer
}
pj.end ();
}
else
System.out.println ("printing canceled!");
}
catch (Exception e)
{
System.out.println (e);
}
}
}
1.3 Un semplice editore di testi
Realizzazione di un semplice editor per file di testo utilizzante le funzionalitá di manipolazione di stringhe presenti
nel AWT.
Listato 1.3 - TextEditor.java: un editore di testi.
/*
* 1.1 version.
*/
import java.awt. *;
import java.awt.event. *;
import java.io. *;
public class TextEditor extends Frame
implements ActionListener, ItemListener
{
boolean inAnApplet = true;
TextArea output;
PopupMenu popup;
String newline;
String clipboard;
public TextEditor ()
{
MenuBar mb;
Menu mfile, medit, m3, m4, m4_1, m5;
MenuItem miopen, miquit, mi3_1, mi3_2, mi3_3, mi3_4, mi4_1_1, mi5_1,
mi5_2, pmi1, pmi2, mi5_1_duplicate;
MenuItem micut, micopy, mipaste, miselect;
CheckboxMenuItem mi2_1;
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (inAnApplet) {
dispose();
} else {
System.exit(0);
}
}
});
newline = System.getProperty ("line.separator");
//Add regular components to the window.
setLayout (new BorderLayout ());
//max space: output
output = new TextArea (5, 30);
output.setEditable (true);
add ("Center", output);
/*
Label label = new Label("Try bringing up"
+ " a popup menu!");
add("North", label);
*/
//Build the menu bar.
mb = new MenuBar ();
setMenuBar (mb);
//Build first menu in the menu bar.
//Specifying the second argument as true
//makes this a tear-off menu.
mfile = new Menu ("File", true);
mb.add (mfile);
miopen = new MenuItem ("Open ...");
mfile.add (miopen);
mfile.addSeparator ();
miquit = new MenuItem ("Quit");
mfile.add (miquit);
//
//Build second menu in the menu bar.
medit = new Menu ("Edit");
mb.add (medit);
mi2_1 = new CheckboxMenuItem("Menu item 2_1");
//
m2.add(mi2_1);
micut = new MenuItem ("Cut");
medit.add (micut);
micopy = new MenuItem ("Copy");
medit.add (micopy);
mipaste = new MenuItem ("Paste");
medit.add (mipaste);
medit.addSeparator ();
miselect = new MenuItem ("Select all");
medit.add (miselect);
/*
//Build help menu.
m5 = new Menu("Help Menu");
mb.setHelpMenu(m5);
mi5_1 = new MenuItem("Menu item 5_1");
mi5_1.setShortcut(new MenuShortcut(KeyEvent.VK_5));
m5.add(mi5_1);
mi5_2 = new MenuItem("Menu item 5_2");
m5.add(mi5_2);
*/
//Make a popup menu.
popup = new PopupMenu ("A Popup Menu");
add (popup);
pmi1 = new MenuItem ("A popup menu item");
popup.add (pmi1);
mi5_1_duplicate =
new MenuItem ("Duplicate of menu item 5_1",
new MenuShortcut (KeyEvent.VK_5));
popup.add (mi5_1_duplicate);
pmi2 = new MenuItem ("An item with a shortcut",
new MenuShortcut (KeyEvent.VK_6));
popup.add (pmi2);
//Build third menu in the menu bar.
m3 = new Menu ("Menu 3");
mb.add (m3);
mi3_1 = new MenuItem ("Menu item 3_1");
m3.add (mi3_1);
mi3_2 = new MenuItem ("Menu item 3_2");
m3.add (mi3_2);
m3.addSeparator ();
mi3_3 = new MenuItem ("Menu item 3_3");
m3.add (mi3_3);
mi3_4 = new MenuItem ("Menu item 3_4");
mi3_4.setEnabled (false);
m3.add (mi3_4);
//Build fourth menu in the menu bar.
m4 = new Menu ("Menu 4");
mb.add (m4);
m4_1 = new Menu ("Submenu 4_1");
m4.add (m4_1);
mi4_1_1 = new MenuItem ("Menu item 4_1_1");
m4_1.add (mi4_1_1);
//Register as an ActionListener for all menu items.
mfile.addActionListener (this);
medit.addActionListener (this);
m3.addActionListener (this);
m4.addActionListener (this);
mi4_1_1.addActionListener (this);
//m4 can’t detect
//submenu actions
//
m5.addActionListener(this);
popup.addActionListener (this);
//Set action commands for a few menu items.
miopen.setActionCommand ("open");
miquit.setActionCommand ("quit");
micut.setActionCommand ("cut");
micopy.setActionCommand ("copy");
mipaste.setActionCommand ("paste");
miselect.setActionCommand ("select");
//
mi5_1.setActionCommand("5_1");
//
mi5_2.setActionCommand("5_2");
pmi1.setActionCommand ("popup item #1");
//
mi5_1_duplicate.setActionCommand("5_1");
pmi2.setActionCommand ("popup item #2");
//Register as ItemListener on checkbox menu item.
//
mi2_1.addItemListener(this);
//Listen for when the popup menu should be shown.
MouseListener listener = new PopupListener ();
addMouseListener (listener);
output.addMouseListener (listener);
//
label.addMouseListener(listener);
}
class PopupListener extends MouseAdapter
{
public void mousePressed (MouseEvent e)
{
maybeShowPopup (e);
}
public void mouseReleased (MouseEvent e)
{
maybeShowPopup (e);
}
private void maybeShowPopup (MouseEvent e)
{
if (e.isPopupTrigger ())
{
popup.show (e.getComponent (),
e.getX (), e.getY ());
}
}
}
public void actionPerformed (ActionEvent e)
{
/*
output.append("\"" + e.getActionCommand()
+ "\" action detected in menu labeled \""
+ ((MenuItem)(e.getSource())).getLabel()
+ "\"." + newline); */
if (e.getActionCommand ().compareTo ("quit") == 0)
System.exit (0);
else if (e.getActionCommand ().compareTo ("open") == 0)
{
FileDialog fd = new FileDialog (this);
fd.show ();
System.out.println (fd.getFile ());
System.out.println (fd.getDirectory ());
if (fd.getFile () != null)
{
output.setText ("");
setTitle ("TextEdit: " + fd.getDirectory () + fd.getFile ());
loadFile (fd.getDirectory () + fd.getFile ());
}
}
else if (e.getActionCommand ().compareTo ("select") == 0)
{
output.selectAll ();
}
else if (e.getActionCommand ().compareTo ("copy") == 0)
{
clipboard = output.getSelectedText ();
System.out.println (clipboard);
}
else if (e.getActionCommand ().compareTo ("cut") == 0)
{
clipboard = output.getSelectedText ();
output.replaceRange ("", output.getSelectionStart (), output.getSelectionEnd ());
}
else if (e.getActionCommand ().compareTo ("paste") == 0)
{
output.insert (clipboard, output.getCaretPosition ());
}
}
public void itemStateChanged (ItemEvent e)
{
output.append ("Item state change detected on item \""
+ e.getItem ()
+ "\" (state is "
+ ((e.getStateChange () ==
ItemEvent.SELECTED) ?
"selected)."
: "deselected).")
+ newline);
}
public void loadFile (String fname)
{
int r;
char[] ch = new char[1];
try
{
FileInputStream fin = new FileInputStream (fname);
try
{
//
while ((r = fin.read ()) != -1)
{
System.out.println (r);
ch[0] = (char) r;
output.append (new String (ch));
}
}
catch (EOFException e)
{
}
fin.close ();
}
catch (IOException e)
{
System.out.println ("IOex");
}
}
public static void main (String[]args)
{
TextEditor window = new TextEditor ();
window.inAnApplet = false;
window.setTitle ("TextEditor:");
window.setSize (450, 200);
window.setVisible (true);
}
}
Capitolo 2
Multiprogrammazione.
In java esiste la possibilitá di definire threads, ovvero diverse linee di esecuzione presenti all’interno di uno stesso
programma. È quindi possibile sviluppare programmi in cui diversi elementi vengono eseguiti in maniera concorrente.
2.1 Loops
Esempio di programmazione concorrente in cui viene creato un certo numero di threads, ciascuno dei quali esegue un
ciclo infinito di stampa di un messaggio.
Listato 2.1 - Loops.java: cicli infiniti
class Loops implements Runnable{
Thread loopThread = null;
String name;
Loops(String lname){
name=lname;
}
public void start() {
if (loopThread == null) {
loopThread = new Thread(this,name);
loopThread.start();
}
}
public void run(){
while (true) System.out.println(name+" ") ; // loop for ever
}
public static void main (String[] args) {
int nargs = args.length;
int nloop;
if (nargs<1) nloop=4;
else nloop= Integer.parseInt(args[0]);
System.out.println("Creating "+nloop+" loops.");
for(int i=0; i<nloop;i++){
System.out.println("Starting loop: LOOP"+i);
new Loops("LOOP"+i).start();
}
}
}
2.2 Loops temporizzato
analogo a quello della sezione precedente, solo che in questo caso é definito il tempo di ciclo dei singoli
Esempio
threads.
Listato 2.2 - TimedLoop.java: cicli temporizzati
class TimedLoop implements Runnable{
Thread clockThread = null;
public void start() {
if (clockThread == null) {
clockThread = new Thread(this, "Loop");
clockThread.start();
}
}
public void join(){
try {
clockThread.join();
} catch (InterruptedException e){
}
}
public void run(){
long t0,t1,t2;
t0=System.currentTimeMillis();
while (true) {
t1=System.currentTimeMillis();
//while (t1-t0<250) t1=System.currentTimeMillis();
System.out.println(t1-t0);
t0=t1;
t2=System.currentTimeMillis();
try {
Thread.sleep(250-(t2-t1));
} catch (InterruptedException e){
}
}
}
public static void main (String[] args) {
TimedLoop tl=
new TimedLoop();
tl.start();
//
tl.join();
}
}
Capitolo 3
Remote Method Invocation
Remote Method Invocation è un sistema che permette ad applicazioni scritte in Java ed eseguite come singoli processi
di comunicare tra di loro secondo un paradigma basato sulla programmazione ad oggetti. È possibile scambiare oggetti
tra una applicazione e l’altra e invocare metodi su oggetti remoti, ovvero che risiedono in una applicazione che non
è quella che invoca l’esecuzione del metodo. Essenzialmente è un estensione dell’ambiente di oggetti fornito dal
linguaggio Java ad un insieme di macchine virtuali Java che attraverso lo scambio di informazioni condividono lo
spazio degli oggetti definiti remoti.
La programmazione ed utilizzo di oggetti remoti su cui sia possibile invocare dei metodi da parte di altre applicazioni non è cos í immediata e semplice, come nel caso di oggetti locali, ma deve seguire una procedura ben definita:
per ogni oggetto i cui metodi possano essere invocati in maniera remota si deve definire un interfaccia che
definisca tali metodi
ogni oggetto che possa essere scambiato tra applicazioni differenti deve implementare l’interfaccia java.io.Serializable
realizzare gli oggetti che implementano le interfaccie remote
Inoltre nella fase di generazione di una applicazione, dopo aver compilato con successo tutte le classi, si deve eseguire in comando rmic ClassName su tutte le classi che definiscono oggetti remoti. Questo comando crea due file:
ClassName Skel.class necessario all’applicazione in cui l’oggetto remoto risiede e ClassName Stub.class che viene
utilizzato da tutte quelle applicazioni che vogliano interagire con l’oggetto in maniera remota.
Infine si deve attivare, con il comando rmiregistry, un servizio di naming per gli oggetti remoti. Tale servizio
permette ad applicazioni che vogliano utilizzare oggetti remoti di ottenere un riferimento a tali oggetti, che deveno
essere stati registrati in precedenza con il servizio stesso. É da notarsi che per motivi di sicurezza, oggetti remoti
possono essere registrati solamente con un servizio di naming che sia attivo sul medesimo host su cui l’oggetto remoto
risiede.
3.1 Client Server
Semplice esempio di una architettura client-server realizzata utilizzando le funzionalitá del RMI. In questo caso vi è
un server, realizzato come oggetto remoto, che fornisce dei servizi ad uno o piú clienti. I servizi forniti ai clienti sono
solamente due: il nome del server stesso ed una semplice elaborazione di informazione ricevuta dal cliente.
Figura 3.1: Architettura client server
La figura 3.1 mostra le relazioni ed il loro ordine di occorrenza tra i vari moduli che costituiscono l’intera configurazione. All’inizio il server registra se stesso con il servizio di naming (0), poi un cliente, dopo aver ottenuto un
riferimento al server interrogando il servizio di naming (1), puó interagire con il server richiedendo un servizio (2).
L’interfaccia ServerIntf mostrata nel listato 3.1 definisce i metodi remoti associati con i due servizi offerti dal
server.
Listato 3.1 - ServerIntf.java: definizione dei serizi
/**
ServerIntf.java
interfaccia che definisce i metodi del server che possono
essere invocati da remoto
*/
import java.rmi. *;
public interface ServerIntf extends Remote
{
public String serverName () throws RemoteException;
/*
ritorna il nome del server
*/
public Datum compute (Datum sth) throws RemoteException;
/*
ritorna un informazione di tipo Datum ottenuta elaborando
un’informazione sempre di tipo Datum ricevuta dal cliente
*/
}
La classe Datum.java rappresenta l’informazione scambiata tra i clienti ed il server ed è costituita da due campi:
una stringa ed un valore numerico.
Listato 3.2 - Datum.java: definizione informazione scambiata
/**
Datum.java
classe che implemeta il tipo di informazione scambiata tra clienti
ed il server.
Tale informazione costituita da due campi:
una stringa ed un valore intero
*/
class Datum implements java.io.Serializable
{
String name = "pluto" /*valore di default */ ;
int value;
/*
costruttore
il parametro s e‘ il valore da assegnare al campo name
*/
Datum (String s)
{
name = s;
}
/**
metodo per assegnare un valore al campo numerico
*/
void setValue (int i)
{
value = i;
}
/**
metodo che ritorna il valore del campo numerico
*/
int getValue ()
{
return value;
}
/**
conversione al tipo String
*/
public String toString ()
{
return name + " " + value;
}
}
Nel listato 3.3 è mostrato il programma per il server
Listato 3.3 - Server.java: il programma server
/**
Server.java
realizzazione del programma server
*/
import
import
import
import
java.util. *;
java.rmi. *;
java.net. *;
java.rmi.server. *;
public class Server extends UnicastRemoteObject
/* tutti gli oggetti che possono essere invocati in maniera remota devono
essere sottoclassi della classe UnicastRemoteObject */
implements ServerIntf
/* interfaccia che definisce i metodi remoti */
{
String myName;
/**
costruttore
*/
public Server () throws RemoteException
{
// il nome del server e’ il nome del host su cui gira
try
{
// ottieni nome dell’host
String fullHostName = InetAddress.getLocalHost ().getHostName ();
// calcola posizione del primo .
int idx = fullHostName.indexOf (’.’);
//estrai nome senza dominio
myName = fullHostName.substring (0, idx);
}
catch (Throwable e)
{
myName = "noHost";
}
}
/**
realizzazione del servizio che ritorna il nome del server
*/
public String serverName ()
{
return myName;
}
/**
realizzazione del servizio che elabora i dati ricevuti dal cliente
*/
public Datum compute (Datum sth)
{
// crea nuovo dato
Datum temp = new Datum ("Server");
// stampa il dato ricevuto dal cliente
System.out.println ("Received " + sth);
// elabora: incrementa di uno il valore ricevuto dal cliente
temp.setValue (sth.getValue () + 1);
// ritorna al cliente il risultato dell’elaborazione
return temp;
}
/**
programma principale
*/
public static void main (String[]args)
{
try
{
/* installa un security manager. E’ obbligatorio
quando si utilizzano le funzionalita’ del RMI */
RMISecurityManager security = new RMISecurityManager ();
System.setSecurityManager (security);
/* crea il server */
Server theServer = new Server ();
/* registra il server nel rmiregistry della macchina locale
con il nome Server */
Naming.rebind ("Server", theServer);
/* stampa messaggio */
System.out.println ("Server ready!");
}
catch (Throwable e)
{
System.out.println ("exception: " + e);
System.exit (1);
}
}
}
mentre nel listato 3.4 quello dei clienti:
Listato 3.4 - Client.java: il programma client
/**
Client.java
realizzazione del programma client
il programma viene attivato con il comando
java Client [-h host]
value
dove host e’ il nome del calcolatore su cui risiede il server.
Se non e’ presente si usa localhost
value e’ il valore intero che verra utilizzato nel dato che il
cliente inviera’ al server
*/
import java.rmi. *;
import java.rmi.server. *;
public class Client
{
/**
programma principale
*/
public static void main (String[]args)
{
String RMIRegHost;/* nome del host su cui e’ il server */
int value; /* valore iniziale campo numerico */
if (args.length < 3)
{
RMIRegHost = "localhost";
value = Integer.parseInt(args[0]);
}
else {
RMIRegHost = args[1];
value = Integer.parseInt(args[2]);
}
try
{
/* installa un security manager. E’ obbligatorio
quando si utilizzano le funzionalita’ del RMI */
RMISecurityManager security =
new RMISecurityManager ();
System.setSecurityManager (security);
/* creazione del dato da inviare al server*/
Datum myDatum = new Datum ("Client");
myDatum.setValue (value);
/* nome del server nel RMI registry*/
String RMIRegName = "Server";
/* URL per accedere al RMI registry
tale url e’ costituito dal protocollo rmi seguito dal nome del host
e dal nome dell’oggetto */
String name = "rmi://" + RMIRegHost + "/" + RMIRegName;
/* interrogazione del RMI registry per ottenere un riferimento al server */
ServerIntf theServer = (ServerIntf) Naming.lookup (name);
/* stampa del nome del server ottenuto attivando un metodo remoto */
System.out.println ("Server name: " + theServer.serverName ());
/* stampa del dato che il cliente invia al server */
System.out.println (myDatum);
/* stampa del dato ricevuto dal server e che si ottiene
utilizzando un metodo remoto */
System.out.println (theServer.compute (myDatum));
}
catch (Throwable e)
{
System.out.println ("exception: " + e);
System.exit (1);
}
}
}
La compilazione di questi moduli aviiene con i seguenti comandi:
javac ServerIntf.java
javac Client.java
javac Server.java
rmic Server
Per l’esecuzione si deve prima attivare il servizio di naming sulla macchina su cui sará disponibile il server con il
comando:
sun450% rmiregistry &
sun450%
dove sun450% è il prompt del calcolatore il cui nome è appunto sun450. Quindi si puó attivare il server con il
comando java Server ottenendo:
sun450% java Server
Server ready!
Clienti possono essere attivati sia sul nodo su cui vi è il server
sun450% java Client 33
Server name: sun450
Client 33
Server 34
sun450%
sia da altri calcolatori (in questo caso dal nodo sun1)
sun1% java Client -h sun450 23
Server name: sun450
Client 23
Server 24
sun1%
E’ importante ricordarsi che quando si fanno delle modifiche al codice, si deve terminare il processo rmiregistry che
fornisce il servizio di naming altrimenti non si ottiene un funzionamento corretto. Per fare questo si deve, utilizzando
il comando ps, individuare tale processo ed il processo java che ha attivato
sun450% ps
PID TTY
344 pts/0
377 pts/0
384 pts/0
sun450%
TIME
0:00
0:00
0:01
CMD
csh
rmiregis
java
ed abortire il processo java
sun450% kill -9 384
sun450% Killed
[1]
Exit 137
sun450%
rmiregistry
3.2 Robot Name Service
Estensione del sistema client-server illustrato nella sezione precedente ad un sistema con un numero illimitato di
clients e servers.
É stato aggiunto un server di gestione dei server per permettere ai clients di ottenere un riferimento al server a cui
vogliono collegarsi.
F
!#"%$'&)(+*-,/.10/&32
G
!#(A<B4-0/(+CED0
F
G
HHH9
G
I
J
!#4657480
9;:+<(
= (+&3>?,/@1(
I
!#4657480
F
G
HHH"
Figura 3.2: Architettura del sistema RobotNameService
Listato 3.5 - RobotNameServiceIntf.java: servizi del Robot Name Service
/**
RobotNameServiceIntf.java
interfaccia che definisce i metodi remoti associati ai servizi
forniti dal Robot Name Service
*/
import java.rmi. *;
public interface RobotNameServiceIntf extends Remote
{
public void register (String name, RobotIntf robot) throws RemoteException;
/* registra l’oggetto robot con il nome name */
public RobotIntf lookup (String name) throws RemoteException;
/* ritorna un riferimento al robot associato al nome name */
}
Listato 3.6 - RobotNameService.java: il programma per il Robot Name Service
/**
RobotNameService.java
servizio di naming per robot mobili
mantiene un associazione robot-nome e permette di inserie un associazione
o di cercare il robot associato ad un dato nome
*/
import
import
import
import
java.util. *;
java.rmi. *;
java.rmi.server. *;
java.rmi.registry. *;
public class RobotNameService extends UnicastRemoteObject
implements RobotNameServiceIntf
{
/* costante che indica il massimo numero di associazioni */
static final int max = 10;
String names[];
/* nomi dei robot */
RobotIntf robots[];
/* riferimenti ai robot */
int idx;
/* indice che indica il numero di associazioni inserite */
/**
costruttore
*/
public RobotNameService () throws RemoteException
{
super ();
idx = 0;
/* nessuna associazione */
/* alloca array per i dati delle associazioni */
names = new String[max];
robots = new RobotIntf[max];
}
/**
inserimento di una associazione robot-nome
*/
public void register (String name, RobotIntf robot)
{
System.out.println ("Registering " + name);
names[idx] = name;
robots[idx++] = robot;
}
/*
ricerca di un robot, dato un nome
*/
public RobotIntf lookup (String name)
{
int i;
System.out.println ("Lookup " + name);
for (i = 0; i < idx; i++)
if (names[i].equals (name))
break;
if (i != idx)
return robots[i];
else
return null;
}
/**
programma principale
*/
public static void main (String[]args)
{
try
{
/* installa un security manager. E’ obbligatorio
quando si utilizzano le funzionalita’ del RMI */
RMISecurityManager security =
new RMISecurityManager ();
System.setSecurityManager (security);
RobotNameService rns = new RobotNameService ();
/* ottieni riferimento al rmiregistry */
Registry theRegistry = LocateRegistry.getRegistry ();
/* registra il Robot Name Service nel rmiregistry con il nome RNS */
theRegistry.rebind ("RNS", rns);
/* stampa messaggio */
System.out.println ("The RNS is ready!");
/* d’ora in poi attendi che qualcuno richieda un tuo servizio */
}
catch (Throwable e)
{
System.out.println ("exception: " + e);
System.exit (1);
}
}
}
Listato 3.7 - RobotIntf.java: i comandi del robot
/**
RobotIntf.java
interfaccia che definisce i etodi remoti associati alle azioni che un robot mobile
puo’ eseguire
*/
import java.rmi. *;
public interface RobotIntf extends Remote
{
public RobotState getStatus () throws RemoteException;
/* ritorna lo stato del robot */
public void go () throws RemoteException;
/* abilita il robot a muoversi */
public void stop () throws RemoteException;
/* ferma il robot */
K
public void reset () throws RemoteException;
/* esegue un reset dello stato del robot */
}
Listato 3.8 - RobotState.java: lo stato del robot
/**
RobotState.java
rappresentazione dello stato di un robot
*/
class RobotState implements java.io.Serializable
{
/*nome del robot */
String name;
/* valori dell’odometria */
float x, y, th;
/**
costruttore
*/
RobotState (String aname)
{
name = aname;
x = y = th = 0.0f;
}
/**
reset dell’odometria
*/
synchronized void reset ()
{
x = y = th = 0.0f;
}
/**
modifica dell’odometria
*/
synchronized void update (float new_x, float new_y, float new_th)
{
x = new_x;
y = new_y;
th = new_th;
}
/* i due metodi precedenti sono synchronized per evitare conflitti
nell’assegnazione dei valori alle medesime variabili */
/**
conversione a stringa
*/
public String toString ()
{
return name + " " + x + " " + y + " " + th;
}
}
Listato 3.9 - Robot.java: il programma robot
/**
Robot.java
il programma robot e’ una simulazione di un robot mobile.
Una volta registratosi con il Robot Name Service il robot
rimane in attesa che qualcuno richieda da remoto l’esecuzione
di uno dei metodi definiti nell’interfaccia RobotIntf.
Alcuni di questi metodi possono attivare o disattivare un thread che
rappresenta il ciclo di controllo dove lo stato del robot viene
modficato.
*/
import
import
import
import
import
java.util. *;
java.net. *;
java.rmi. *;
java.rmi.server. *;
java.rmi.registry. *;
public class Robot extends UnicastRemoteObject
implements RobotIntf
{
/* costante con il nome del host su cui vi e’ il rmiregistry */
static final String RNSHost = "sun450.ladseb.pd.cnr.it";
/* variabili locali del robot */
String myName;
/* nome del robot */
RobotState myState;
/* stato del robot */
RobotControl cloop;
/* thread del ciclo di controllo */
/**
costruttore
*/
public Robot (String name) throws RemoteException
{
myName = name;
myState = new RobotState (name);
cloop = new RobotControl ();
}
/**
implementazione dei metodi dell’interfaccia RobotIntf
sono i metodi che possono essere attivati da remoto
*/
public RobotState getStatus ()
{
return myState;
}
public void go ()
{
System.out.println (myName + " starts moving");
cloop.start ();
}
public void stop ()
{
cloop.stop ();
System.out.println (myName + " stopped");
}
K
public void reset ()
{
myState.reset ();
}
/**
classe che realizza il ciclo di controllo
esempio di inner class, ovvero definita all’interno di un’altra classe
*/
class RobotControl implements Runnable
{
/* thread per il controllo */
Thread controlThread;
/**
attivazione del ciclo
*/
public void start ()
{
if (controlThread == null)
{
controlThread = new Thread (this, "cloop");
controlThread.start ();
}
}
/**
disattivazione del ciclo
*/
public void stop ()
{
controlThread = null;
}
/**
routine che costituisce il coprpo del thread
*/
public void run ()
{
Thread myThread = Thread.currentThread ();
/* cicla finche’ controlThread non viene messo a null */
while (myThread == controlThread)
{
/* stampa nome del robot */
System.out.println (Robot.this.myName);
/* modifica stato incrementando coordinate x e y */
Robot.this.myState.update (Robot.this.myState.x + 50.3f,
Robot.this.myState.y + 33.3f,
0.0f);
try
{
/* dorme per 2.5 seconds */
Thread.sleep (2500);
}
catch (InterruptedException e)
{
}
}
}
}
/**
programma principale
*/
public static void main (String[]args)
{
/* se non vi e’ il nome del robot esci con errore */
if (args.length < 1)
{
System.out.println ("Robot name needed");
System.exit (1);
}
/* prendi il proprio nome dalla linea di comando */
String name = args[0];
try
{
/* installa un security manager. E’ obbligatorio
quando si utilizzano le funzionalita’ del RMI */
RMISecurityManager security =
new RMISecurityManager ();
System.setSecurityManager (security);
/* crea l’oggetto robot */
Robot thisRobot = new Robot (name);
/* ottieni riferimento al rmiregistry */
Registry theRNSRegistry = LocateRegistry.getRegistry (RNSHost);
/* ottieni riferimento al Robot Name Service meorizzato nel rmiregistry
con il nome RNS */
RobotNameServiceIntf rns = (RobotNameServiceIntf) theRNSRegistry.lookup ("RNS");
/* registra se stesso con il Robot Name Service */
rns.register (name, (RobotIntf) thisRobot);
/* stampa per avvertire che e’ ready */
System.out.println ("The robot " + name + " is ready!");
/* d’ora in poi il robot attende che qualcuno richieda l’esecuzione
di un metodo remoto */
}
catch (Throwable e)
{
System.out.println ("exception: " + e);
System.exit (1);
}
}
}
Listato 3.10 - RemoteCnt.java:il controllo remoto
/**
RemoteCnt.java
programma di controllo di un robot.
si attiva con il comando:
java RemoteCnt nomerobot
il programma, dopo aver ottenuto un riferimento al robot associato al nome nomerobot
dal Robot Name Service, permette all’utente di interagire con il robot attraverso
un insieme di comandi che vengono eseguiti dal robot. Inoltre stampa il tempo
di esecuzione di tali comandi. Tale tempo comprende anche l’overhead dovuto
al protocollo RMI
*/
import java.io. *;
import java.rmi. *;
import java.rmi.registry. *;
public class RemoteCnt
{
/* costante con il nome del host su cui vi e’ il rmiregistry */
static final String RNSHost = "sun450.ladseb.pd.cnr.it";
/* riferimento al robot controllato */
static RobotIntf theRobot;
/**
stampa del menu utente
*/
static void printMenu ()
{
System.out.println ();
System.out.println ("RemoteCnt Menu");
System.out.println ();
System.out.println ("i : print robot’s status");
System.out.println ("g : motion start");
System.out.println ("s : motion stop");
System.out.println ("r : reset the robot");
System.out.println ("q : quit the program");
}
/**
gestione dell’interfaccia utente
*/
static void userInterface ()
{
RobotState theState;
boolean loopFlag = true;
int ch;
long t0, t1;
printMenu ();
try
{
while (loopFlag)
{
System.out.print (">");
/* lettura comando (un carattere) */
ch = System.in.read ();
/* scelta del comando da eseguire */
switch (ch)
{
case ’q’:
{
loopFlag = false;
break;
}
case ’i’:
{
t0 = System.currentTimeMillis ();
theState = theRobot.getStatus (); /* esecuzione remota */
t1 = System.currentTimeMillis ();
System.out.println ("Info: " + theState);
System.out.println ("Done in " + (t1 - t0) + " millisec.");
break;
}
case ’g’:
{
t0 = System.currentTimeMillis ();
theRobot.go ();
/* esecuzione remota */
t1 = System.currentTimeMillis ();
System.out.println ("Done in " + (t1 - t0) + " millisec.");
break;
}
case ’s’:
{
t0 = System.currentTimeMillis ();
theRobot.stop (); /* esecuzione remota */
t1 = System.currentTimeMillis ();
System.out.println ("Done in " + (t1 - t0) + " millisec.");
break;
}
case ’r’:
{
t0 = System.currentTimeMillis ();
theRobot.reset (); /* esecuzione remota */
t1 = System.currentTimeMillis ();
System.out.println ("Done in " + (t1 - t0) + " millisec.");
break;
}
default:
printMenu ();
}
/* leggi fino a fine linea */
while (ch != ’\n’)
ch = System.in.read ();
}
}
catch (IOException e)
{
}
}
/**
programma principale
*/
public static void main (String[]args)
{
/* esci segnalando errore se non vi e’ il nome del robot */
if (args.length < 1)
{
System.out.println ("Robot name needed");
System.exit (1);
}
try
{
/* installa un security manager. E’ obbligatorio
quando si utilizzano le funzionalita’ del RMI */
RMISecurityManager security =
new RMISecurityManager ();
System.setSecurityManager (security);
/* ottieni riferimento al rmiregistry */
Registry theRMIRegistry = LocateRegistry.getRegistry (RNSHost);
/* ottieni riferimento al Robot Name Service meorizzato nel rmiregistry
con il nome RNS */
RobotNameServiceIntf rns = (RobotNameServiceIntf) theRMIRegistry.lookup ("RNS");
/* ottieni riferimento al robot dal Robot Name Service */
theRobot = rns.lookup (args[0]);
/* se il riferimento non e’ nullo attiva l’interfaccia utente */
if (theRobot != null)
userInterface ();
/* altrimenti esci con messaggio di errore */
else
System.out.println ("Robot " + args[0] + " not found in the RNS");
}
catch (Throwable e)
{
System.out.println ("exception: " + e);
System.exit (1);
}
}
}
La compilazione di questi moduli aviiene con i seguenti comandi:
javac RobotIntf.java
javac RobotNameServiceIntf.java
javac RemoteCnt.java
javac Robot.java
javac RobotNameService.java
rmic Robot
rmic RobotNameService
3.3 Information Delivery Service
Esempio di scambio di informazione da uno a molti attraverso l’uso di un server per la distribuzione di tale informazione.
Listato 3.11 - InfoIntf.java: interfaccia per ricevere l’informazione
/**
InfoIntf.java
*/
OZ\[]
^%R_T'`RaQY
b RQ6`UTcR
f
LNMPOQ6RSUTVXWQY
n
g
l
mmmm
OZ\[]UdeVRaQ
f
g
hih3hkj
Figura 3.3: Architettura del sistema di InfoDelivery
import java.rmi.*;
public interface InfoIntf extends Remote {
public void setInfo(Info sth) throws RemoteException;
public void test(String n) throws RemoteException;
}
Listato 3.12 - InfoDeliveryServiceIntf.java: interfaccia per il server
/**
InfoDeliveryServiceIntf.java
*/
import java.rmi.*;
public interface InfoDeliveryServiceIntf extends Remote {
public boolean register(InfoIntf user) throws RemoteException;
public Info getInfo() throws RemoteException;
}
Listato 3.13 - InfoDeliveryService.java: il programma server
/**
InfoDeliveryService.java
*/
import
import
import
import
java.util.*;
java.rmi.*;
java.rmi.server.*;
java.rmi.registry.*;
public class InfoDeliveryService extends UnicastRemoteObject
implements InfoDeliveryServiceIntf {
static final public int MAX_USERS=10;
InfoIntf users[];
int idx;
SendInfoLoop infoCnt;
Info myInfo;
public InfoDeliveryService() throws RemoteException{
super();
idx=0;
users = new InfoIntf[MAX_USERS];
myInfo = new Info();
myInfo.genera();
infoCnt = new SendInfoLoop();
}
public Info getInfo(){
return myInfo;
}
synchronized public boolean register(InfoIntf user){
System.out.println("Registering a user");
if (idx==MAX_USERS) return false;
users[idx++]=user;
return true;
}
synchronized int getNumUsers(){
return idx;
}
void start(){
if (infoCnt!=null)infoCnt.start();
}
class SendInfoLoop implements Runnable{
Thread infoLoopThread;
public void start(){
if (infoLoopThread==null) {
infoLoopThread = new Thread(this,"infoloop");
infoLoopThread.start();
}
}
public void stop(){
infoLoopThread=null;
}
public void run(){
Thread myThread = Thread.currentThread();
int local_idx=0;
while (myThread == infoLoopThread){
try{
myInfo.genera();
local_idx= getNumUsers();
for(int i=0;i<local_idx;i++)
try{
if (users[i]!=null) users[i].setInfo(myInfo);
} catch (Throwable e) {
System.out.println("user error " +e);
users[i]=null;
}
Thread.sleep(5000); // waits 5 seconds
} catch (Throwable e){
System.out.println("excpetion: "+e);
System.exit(1);
}
}
}
}
public static void main(String[] args){
try {
RMISecurityManager security =
new RMISecurityManager();
System.setSecurityManager(security);
InfoDeliveryService ids = new InfoDeliveryService();
Registry theRegistry = LocateRegistry.getRegistry();
theRegistry.rebind("IDS",ids);
ids.start();
System.out.println("IDS is ready!");
} catch (Throwable e) {
System.out.println("exception: "+e);
System.exit(1);
}
}
}
Listato 3.14 - InfoUser.java: programma cliente del InfoDeliveryService
/**
InfoUser.java
args[0] = hostname whre the delivery service runs, if
the argument is present, otherwise localhost is used
*/
import
import
import
import
import
java.util.*;
java.net.*;
java.rmi.*;
java.rmi.server.*;
java.rmi.registry.*;
public class InfoUser extends UnicastRemoteObject
implements InfoIntf {
static String IDSHost="localhost";
Info myInfo;
public InfoUser() throws RemoteException{
super();
}
public void setInfo(Info newInfo){
//myInfo.copy(newInfo);
System.out.println("Info received :"+newInfo);
}
public void test(String n){
System.out.println("test received :"+n);
}
public static void main(String[] args){
if (args.length>0) IDSHost = args[0];
try {
RMISecurityManager security =
new RMISecurityManager();
System.setSecurityManager(security);
InfoUser thisUser = new InfoUser();
Registry theRNSRegistry = LocateRegistry.getRegistry(IDSHost);
InfoDeliveryServiceIntf ids =(InfoDeliveryServiceIntf) theRNSRegistry.lookup("IDS");
//System.out.println(ids.getInfo());
if (ids.register((InfoIntf)thisUser))
System.out.println("Waiting for info...");
else {
System.out.println("Delivery Service not available");
System.exit(1);
}
} catch (Throwable e) {
System.out.println("exception: "+e);
System.exit(1);
}
}
}
Capitolo 4
Autoreferenzialitá
Nel linguaggio java é possibile accedere e manipolare gli elementi, quali variabili e metodi, presenti nelle classi che
definiscono gli oggetti utiliizati in un particolare programma. É cosí possibile costruire programmi che funzionano in
maniera indipendente dagli oggetti che devono manipolare o con cui devono interagire.
4.1 Matrici
In questa sezione viene illustrato un programma per eseguire operazioni matriciali che opera in maniera indipendente
dal tipo (intero, reale o complesso) degli elementi della matrice.
Listato 4.1 - Matrix.java - superclass delle matrici
import java.lang.reflect.*;
/**
* superclass per le matrici
*
* @author P.Bison Copyright 1997
*
*/
public
class Matrix {
int righe;
int colonne;
Object[][] data;
/* numero di righe */
/* numero di colonne */
/* constructors */
protected Matrix (){
righe=colonne=0;
data=null;
}
protected Matrix (int n,int m){
righe=n;
colonne=m;
data=new Object[n][m];
}
protected void setMatrix(int n, int m) {
righe=n;
colonne=m;
data=new Object[n][m];
}
/**
* somma due matrici
* @param x il secondo operando della somma
* @return una matrice il cui valore e’ this+x
*/
public Matrix add (Matrix x)
throws Exception
{
Class methodclass;
Method addmethod = null;
Class partypes[]= new Class[3];
Object parlist[] = new Object[3];
boolean trovato=true;
Matrix oper1,oper2,ris;
int i,j;
K
if ((righe != x.righe) || (colonne != x.colonne))
throw new ArithmeticException ("Incompatible matrix");
// ottieni la classe dell’oggetto (this)
// e guarda se ha il metodo
oper1=this; oper2=x;
methodclass= this.getClass();
partypes[0] = Class.forName("java.lang.Integer");
partypes[1] =Class.forName("java.lang.Integer");
partypes[2] = x.data[0][0].getClass();
try {
addmethod= methodclass.getMethod("addElement",partypes);
} catch (NoSuchMethodException exceptionRef) {
trovato = false;
}
if (!trovato) {
// cerca nella classe del parametro (x)
trovato = true;
oper1=x; oper2 = this;
methodclass= x.getClass();
partypes[2] = this.data[0][0].getClass();
try {
addmethod= methodclass.getMethod("addElement",partypes);
} catch (NoSuchMethodException exceptionRef) {
trovato = false;
}
}
if (trovato) {
ris = (Matrix) methodclass.newInstance(); // alloca risultato
ris.setMatrix(righe,colonne); //
for(i=0;i<righe;i++)
for(j=0;j<colonne;j++) {
parlist[0]=new Integer(i);
parlist[1]=new Integer(j);
parlist[2]=oper2.data[i][j];
ris.data[i][j] = addmethod.invoke(oper1,parlist);
}
return ris;
}
else return null;
}
/**
* converte una matrice in una stringa
* @return una stringa che rappresenta il numero complesso
*/
public String toString ()
{
String matrep = "[";
int i, j;
for (i = 0; i < righe; i++)
{
matrep += "[";
for (j = 0; j < colonne; j++)
{
matrep = matrep + (data[i][j]==null ? "NULL" : data[i][j].toString());
if (j != colonne - 1)
matrep += ",";
}
matrep += "]";
}
matrep += "]";
return matrep;
}
/**
* metodo principale
*/
public static void main (String[]args)
throws Exception
{
Matrix mat1,mat2,mat3,ris;
double[][] a ={{1.2, -0.75, 1.8},
{-0.99, 3.45, -4.89},
{-9.8, 0.08, 1.98}};
int[][] a1 ={{1, 0, 0},
{0, 1, 0},
{0, 0, 1}};
mat1= new MatrixInt(a1); // alloca matrice intera
System.out.println("mat1 = "+mat1);
mat2= new MatrixDouble(a); // alloca matrice double
System.out.println("mat2 = "+mat2);
mat3= new MatrixComplex(a,a); // alloca matrice complessa
System.out.println("mat3 = "+mat3);
ris= mat1.add(mat1); // somma due matrici intere
System.out.println("mat1 + mat1 = "+ris);
ris= mat2.add(mat2); // somma due matrici double
System.out.println("mat2 + mat2 = "+ris);
ris= mat1.add(mat2); // somma una matrice intera con una double
System.out.println("mat1 + mat2 = "+ris);
ris= mat1.add(mat3); // somma una matrice intera con una complex
System.out.println("mat1 + mat3 = "+ris);
ris= mat3.add(mat2); // somma una matrice complex con una double
System.out.println("mat3 + mat2 = "+ris);
}
} // end of class Matrix
Listato 4.2 - MatrixInt.java - matrici con elementi interi
public class MatrixInt extends Matrix {
public MatrixInt(int n, int m) {
super(n,m);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) data[i][j] = new Integer(0);
}
public MatrixInt() {
super();
}
public MatrixInt (int[][]initdata)
{
super(initdata.length,initdata[0].length);
for (int i = 0; i < righe; i++)
for (int j = 0; j < colonne; j++)
data[i][j] = new Integer(initdata[i][j]);
}
public Integer addElement(Integer intI, Integer intJ, Integer x){
int i = intI.intValue();
int j = intJ.intValue();
return new Integer(((Integer)data[i][j]).intValue()+x.intValue());
}
}
/* end of class MatrixInt */
Listato 4.3 - MatrixDouble.java - matrici con elementi reali
public class MatrixDouble extends Matrix {
public MatrixDouble() {
super();
}
public MatrixDouble(int n, int m) {
super(n,m);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) data[i][j] = new Double(0);
}
public MatrixDouble (double[][]initdata)
{
super(initdata.length,initdata[0].length);
for (int i = 0; i < righe; i++)
for (int j = 0; j < colonne; j++)
data[i][j] = new Double(initdata[i][j]);
}
public Double addElement(Integer intI, Integer intJ, Double x){
int i = intI.intValue();
int j = intJ.intValue();
return new Double(((Double)data[i][j]).doubleValue()+x.doubleValue());
}
public Double addElement(Integer intI, Integer intJ, Integer x){
int i = intI.intValue();
int j = intJ.intValue();
return new Double(((Double)data[i][j]).doubleValue()+x.intValue());
}
}
Listato 4.4 - MatrixComplex.java - matrici con elementi complessi
public class MatrixComplex extends Matrix {
public MatrixComplex() {
super();
}
public MatrixComplex(int n, int m) {
super(n,m);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) data[i][j] = new Complex();
}
public MatrixComplex(double[][] initReale, double[][] initImm) {
super(initReale.length,initReale[0].length);
for (int i = 0; i < righe; i++)
for (int j = 0; j < colonne; j++)
data[i][j] = new Complex(initReale[i][j],initImm[i][j]);
}
public Complex addElement(Integer intI, Integer intJ, Complex x){
int i = intI.intValue();
int j = intJ.intValue();
return ((Complex)data[i][j]).add(x);
}
public Complex addElement(Integer intI, Integer intJ, Double x){
int i = intI.intValue();
int j = intJ.intValue();
return new Complex(((Complex)data[i][j]).parteReale()+x.doubleValue(),
((Complex)data[i][j]).parteImmaginaria());
}
public Complex addElement(Integer intI, Integer intJ, Integer x){
int i = intI.intValue();
int j = intJ.intValue();
return new Complex(((Complex)data[i][j]).parteReale()+x.intValue(),
((Complex)data[i][j]).parteImmaginaria());
}
}
Il programma principale della classe Matrix produce la seguente uscita:
mat1 = [[1,0,0][0,1,0][0,0,1]]
mat2
mat3
mat1
mat2
mat1
mat1
mat3
=
=
+
+
+
+
+
[[1.2,-0.75,1.8][-0.99,3.45,-4.89][-9.8,0.08,1.98]]
[[1.2+i1.2,-0.75+i-0.75,1.8+i1.8][-0.99+i-0.99,3.45+i3.45,-4.89+i-4.89][-9.8+i-9.8,0.08+i0.08,1.98+i1.98]]
mat1 = [[2,0,0][0,2,0][0,0,2]]
mat2 = [[2.4,-1.5,3.6][-1.98,6.9,-9.78][-19.6,0.16,3.96]]
mat2 = [[2.2,-0.75,1.8][-0.99,4.45,-4.89][-9.8,0.08,2.98]]
mat3 = [[2.2+i1.2,-0.75+i-0.75,1.8+i1.8][-0.99+i-0.99,4.45+i3.45,-4.89+i-4.89][-9.8+i-9.8,0.08+i0.08,2.98+i1.98]]
mat2 = [[2.4+i1.2,-1.5+i-0.75,3.6+i1.8][-1.98+i-0.99,6.9+i3.45,-9.78+i-4.89][-19.6+i-9.8,0.16+i0.08,3.96+i1.98]]
4.2 Calcolo tempo di esecuzione
In questo esempio si mostra come é possibile chiamare il metodo main di una classe qualunque, non definita al tempo
di compilazione, per calcolarne il tempo di esecuzione.
Listato 4.5 - Bench.java - tempo di esecuzione di un metodo main.
import java.lang.reflect.*;
/**
* attivazione del metodo main di una classe.
*
* @author P.Bison Copyright 1997
*/
public class Bench{
/**
* programma principale. Esempio di attivazione di un metodo di una classe
* il cui nome e’ dato in una stringa. L’esempio calcola il tempo
* di esecuzione in millisecondi del metodo main eventualmente presente
* nella classe specificata. Se vi sono argomenti, il primo viene preso
* come il nome della classe, mentre i rimanenti sono passati al main di tale
* classe. Se non vi sono argomenti si usa la classe Test.
*/
public static void main(String[] args) {
String classname="Test";
Class testclass;
Method mainmethod;
Class partypes[]= new Class[1];
Object mainparlist[] = new Object[1];
String[] mainargs = new String[0];
long t1,t0;
int i;
// eventuale primo argomento e’ il nome della classe
// il cui main deve essere eseguito
if (args.length > 0) classname = args[0];
// ottieni un oggetto rappresentate tale classe
try {
testclass= Class.forName(classname);
} catch (ClassNotFoundException excrptRef) {
System.out.println("Classe "+classname+" non trovata!");
return;
}
// ottieni la classe corrispondente al parametro args
partypes[0]= args.getClass();
// ottieni il metodo main per la data classe
// se non esiste viene segnalato un errore ed il programma
// termina
try {
mainmethod= testclass.getMethod("main",partypes);
} catch (NoSuchMethodException excrptRef) {
System.out.println("Metodo main non trovato!");
return;
}
// se vi sono argomenti crea una nuova lista di argomenti
// eliminando il primo (nome della classe)
if (args.length > 1) {
mainargs = new String[args.length-1];
for (i=0;i<args.length-1;i++) mainargs[i]=args[i+1];
}
mainparlist[0]=mainargs;
t0 = System.currentTimeMillis();
// esegui il metodo main
// segnala errore in caso in cui non venga eseguito per
// qualche motivo
try {
mainmethod.invoke(null,mainparlist);
} catch (IllegalAccessException excrptRef) {
System.out.println("Accesso illegale!");
return;
}
catch (IllegalArgumentException excrptRef) {
System.out.println("Argomenti illegali!");
return;
}
catch (InvocationTargetException exRef) {
System.out.print("Eccezione dal main: ");
System.out.println(exRef.getTargetException().toString());
return;
}
// calcola il tempo
t1 = System.currentTimeMillis();
System.out.println("Time: " + (t1 - t0) + " millisec.");
System.out.println();
}
} // end of class Bench
Listato 4.6 - Test.java - classe di test per il programma Bench.
public class Test {
public static void main(String[] args) {
System.out.println("Test main");
}
}
Capitolo 5
Programmi nativi
Il linguaggio Java fornisce delle funzionalitá per interfacciare programmi scritti in Java con procedure scritte in altri
linguaggi, principalmente C e C ++. Un programma Java puó quindi attivare procedure scritte in un altro linguaggio
ed attraverso un ben definito metodo di interfaccia scambiare informazioni con esse via parametri e/o valori di ritorno.
Ovviamente questa funzionalitá fa perdere il vantaggio della portabilitá poichè il programma puó essere eseguito
solamente sulla macchina per cui é stato compilato il codice nativo.
Inoltre é possibile per un programma scritto in C o C ++attivare una macchina virtuale Java ed eseguire routine
scritte in Java.
5.1 Stampa messaggio
Quale primo esempio di programma nativo si illustra un programma che stampa un messaggio alla console.
Il programma é costituito da tre file. Il primo definisce la classe SciavoVostro che dichiara un metodo nativo
ed attraverso un segmento di codice che viene eseguito al caricamento della classe carica la libreria che contiene il
codice nativo. La dichiarazione é caratterizzata dall presenza della parola chiave native e dal affto che non vi sono
istruzioni associate al metodo (assenza del blocco oqp ).
Listato 5.1 - SciavoVostro.java - definizione di un metodo nativo.
/**
* implementa la classe SciavoVostro che definisce il metodo nativo
*
* @author P.Bison Copyright 1997
*/
public class SciavoVostro{
public static native void nativeSciavoVostro();
// static initializer
static {
System.loadLibrary("sciavo"); // load the shared library libsciavo.so (Solaris)
}
}
Il secondo programma (listato 5.1.2) definisce una classe il cui main attiva il metodo nativo in tre modi possibili.
Dal punto di vista dell’efficienza, il terzo metodo é quello migliore perché non alloca memoria per un oggetto che
viene utilizzato solamente per attivare il metodo.
Listato 5.2 - Prog.java - attivazione di un metdo nativo.
/**
* esempio di classe che attiva un metodo nativo in tre modi differenti
*
* @author P.Bison Copyright 1997
*/
public class Prog{
/**
* programma principale
*/
public static void main(String[] args){
// varie possibilita’ di invocazione del metodo nativo
// assegnazione di un oggetto della classe SciavoVostro ad una variabile che
//poi viene utilizzata per attivare il metodo nativo
SciavoVostro sciao = new SciavoVostro();
sciao.nativeSciavoVostro();
//creazione di un oggetto senza assegnazione ad una variabile ed invocazione del metodo nativo
new SciavoVostro().nativeSciavoVostro(); // istanzia un oggetto della classe SciavoVostro
// e invoca il metodo nativeSciavoVostro
// attivazione del metodo attraverso la classe stessa.
// In questo caso il metodo deve essere dichiarato static
SciavoVostro.nativeSciavoVostro();
}
}
Infine il terzo file (listato 5.1.3) contiene il codice in C del metodo nativo. L’intestazione della procedura associata
al metodo é standardizzata e deve essere conforme alla seguente struttura:
il tipo del valore di ritorno deve essere JNIEXPORT void JNICALL
il nome della procedura é creato come concatenazione di Java seguito dal nome della classe in cui é dichiarato
il metodo con aggiunto un sottolineato ( ), seguita dal nome del metodo.
In questo caso Java SciavoVostro nativeSciavoVostro
i parametri della procedura devono essere JNIEnv *env, jobject obj
Inoltre si devono includere il file jni.h, che viene fornito dal compilatore Java, e il file SciavoVostro.h, che
viene generato da un apposito programma partendo dal file SciavoVostro.java.
Listato 5.3 - nativeSciavoVostro.c - codice nativo del metodo
#include <jni.h>
#include "SciavoVostro.h"
#include <stdio.h>
JNIEXPORT void JNICALL
Java_SciavoVostro_nativeSciavoVostro(JNIEnv *env, jobject obj)
{
printf("Sciavo vostro!\n");
return;
}
dati questi tre file, i passi che si devono seguire per generare il programma eseguibile sono i seguenti:
1. compilazione dei due programmi in java:
javac SciavoVostro.java
javac Prog.java
2. creazione del file SciavoVostro.h con il comando
javah -jni SciavoVostro
3. creazione della shared library libsciavo.so in Solaris col comando
gcc -G -I r7sutwvxy7zA{}|U~a€ƒ‚ JDK/include/solaris nativeSciavoVostro.c -o
libsciavo.so
A questo punto è possibile attivare ed eseguire il programma con il comando
java Prog
5.2 Somma ed inversione di un array
Listato 5.4 - SumArray.java - classe per la definizione del metodo nativo.
/**
* implementa la classe SumArray che definisce un metodo
* nativo per la somma degli elementi di un vettore di tipo float
* @author P.Bison Copyright 1998
*/
public class SumArray
{
public static native float sumarray (float[]arg);
// static initializer
static
{
System.loadLibrary ("sumarray");
// load the shared library libsumarray.so (Solaris)
}
}
Listato 5.5 - sumarray.c - implementazione in C del metodo.
#include <jni.h>
#include "SumArray.h"
/*
metodo nativo che calcola la somma di un array di tipo float e
capovolge l’ordine degli elementi dell’array stesso
*/
JNIEXPORT jfloat JNICALL
Java_SumArray_sumarray (JNIEnv * env, jobject obj, jfloatArray arr)
{
jsize len = (*env)->GetArrayLength (env, arr);
// calcola dimensione dell’array
int i;
float sum = 0.0f;
float temp;
// ottieni puntatore all’array (eventuale copia o blocco in memoria)
jfloat *body = (*env)->GetFloatArrayElements (env, arr, 0);
// capovolgi i valori dell’array
for (i = 0; i < len / 2; i++)
{
temp = body[i];
body[i] = body[len - 1 - i];
body[len - 1 - i] = temp;
}
// calcola somma dei valori
for (i = 0; i < len; i++)
sum += body[i];
// rilascia l’uso dell’array (eventuale copia nel array originale o sblocco della memoria)
(*env)->ReleaseFloatArrayElements (env, arr, body, 0);
return sum;
// ritorna la somma calcolata
}
Listato 5.6 - Prog.java - programma di prova.
/**
* classe che attiva un metodo nativo
*
* @author P.Bison Copyright 1997
*/
public class Prog
{
/**
* programma principale
*/
public static void main (String[]args)
{
int i;
float[] arr1 = new float[12];
float ris;
for (i = 0; i < arr1.length; i++)
{
arr1[i] = (float) (i * 10.0);
System.out.print (arr1[i] + " ");
}
System.out.println ();
// attivazione metodo nativo
ris = SumArray.sumarray (arr1);
for (i = 0; i < arr1.length; i++)
{
System.out.print (arr1[i] + " ");
}
System.out.println ();
System.out.println (ris);
}
}
Indice dei listati
GUIWindow.java: elementi del AWT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
PrintTest.java: esempio di stampa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TextEditor.java: un editore di testi. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
6
8
Loops.java: cicli infiniti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
TimedLoop.java: cicli temporizzati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
14
ServerIntf.java: definizione dei serizi . . . . . . . . . . . . . . . .
Datum.java: definizione informazione scambiata . . . . . . . . . .
Server.java: il programma server . . . . . . . . . . . . . . . . . .
Client.java: il programma client . . . . . . . . . . . . . . . . . .
RobotNameServiceIntf.java: servizi del Robot Name Service . . .
RobotNameService.java: il programma per il Robot Name Service
RobotIntf.java: i comandi del robot . . . . . . . . . . . . . . . . .
RobotState.java: lo stato del robot . . . . . . . . . . . . . . . . .
Robot.java: il programma robot . . . . . . . . . . . . . . . . . . .
RemoteCnt.java:il controllo remoto . . . . . . . . . . . . . . . . .
InfoIntf.java: interfaccia per ricevere l’informazione . . . . . . . .
InfoDeliveryServiceIntf.java: interfaccia per il server . . . . . . .
InfoDeliveryService.java: il programma server . . . . . . . . . . .
InfoUser.java: programma cliente del InfoDeliveryService . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
16
16
17
19
21
22
23
24
25
27
30
31
31
33
Matrix.java - superclass delle matrici . . . . . . . . .
MatrixInt.java - matrici con elementi interi . . . . . .
MatrixDouble.java - matrici con elementi reali . . . .
MatrixComplex.java - matrici con elementi complessi
Bench.java - tempo di esecuzione di un metodo main.
Test.java - classe di test per il programma Bench. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
35
37
38
39
40
41
SciavoVostro.java - definizione di un metodo nativo. . . . . .
Prog.java - attivazione di un metdo nativo. . . . . . . . . . .
nativeSciavoVostro.c - codice nativo del metodo . . . . . . .
SumArray.java - classe per la definizione del metodo nativo.
sumarray.c - implementazione in C del metodo. . . . . . . .
Prog.java - programma di prova. . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
43
43
44
45
45
46
47
.
.
.
.
.
.
.
.
.
.
.
.