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 . . . . . . . . . . . .