Przykładowe zastosowania języka Java

Aplet DwaZegary

Aplet DwaZegary ilustruje niektóre możliwości Javy takie, jak: wielowątkowość, współpraca z siecią Internet oraz z przeglądarką, w której wyświetlana jest strona WWW zawierająca aplet. Wykorzystano także pojęcie strumieni oraz klasy i komponenty AWT, do których należą: panele, przyciski, zdarzenia, zarządcy widoków.

W aplecie tym łączymy się z serwerem, z którego została załadowana strona go zawierająca. Z tego serwera odczytujemy datę i czas (usługa 'dtime'), następnie odczytujemy datę i czas na naszym komputerze. Te informacje są wykorzystane przy tworzeniu dwu wątków, odpowiadających za: rysowanie odpowiednio: zegara wyświetlającego czas odczytany z serwera i zegara wyświetlającego czas lokalny. Dodatkowo u dołu okna znajduje się przycisk, po naciśnięciu którego mamy możliwość wysłania poczty email, dzięki wykorzystaniu właściwości przeglądarki. Gdy w użytej tu metodzie showDocument (opis, patrz: Przykład 2.44):

showDocument(new URL("mailto:arturt@friko.onet.pl"),"_self");

zmienimy pierwszy parametr na adres strony WWW, naciśnięcie przycisku spowoduje jej wyświetlenie w przeglądarce.

Apletu w trakcie działania przedstawia poniższa ilustracja:

Ilustracja 4-1Wygląd apletu DwaZegary

Aplet korzysta z usługi 'dtime', usługa ta umożliwia sprawdzenie aktualnego czasu i daty na komputerze, z którym sie połączyliśmy.

Dokładny opis usługi można znaleźć w dokumencie RFC867. Ogólnie, korzysta ona z portu nr 13. Komunikacja polega na nawiązaniu połączenia z serwerem i przesłaniu przez niego informacji. Potem następuje przerwanie połączenia.

Tekst apletu zamieszczono poniżej:

import java.util.Date;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
public class DwaZegary extends java.applet.Applet implements Runnable
{
// program napisano tak, aby łatwo można było zwiększyć liczbę 
// rysowanych zegarów 
	static int m_nLiczbaZegarow = 2;	
	Date daty[];
	WatekZegara zegary[];
	Thread    watki[];
	Thread    watekGlowny;
	int    m_nLiczbaWatkowNaStarcie;
	//adresy, źródła daty i czasu
	public String zrodloDanych[] = {"friko.onet.pl", "czas lokalny"}; 

	public void init()
	{
		// użyjemy tej zmiennej później aby sprawdzić czy wszystkie 
		// wątki zostały zabite
		m_nLiczbaWatkowNaStarcie = Thread.activeCount();
		// ustawienie zarządcy rozmieszczenia; zegary będą dodawane 
		// na prawo od ostatniego dodanego 
		setLayout(new BorderLayout());
		// utworzenie panelu zawierającego zegary 
		Panel panel = zegaryGridLayoutPanel();		
		
		// utworzenie tablic zawierających: 
		// 1 wątki rysujące zegary,
		zegary = new WatekZegara[m_nLiczbaZegarow];
		// 2 wątki kontrolujące wykonanie wątków typu WatekZegara,
		watki = new Thread[m_nLiczbaZegarow];
		// 3 informację o czasie i dacie
		daty = new Date[m_nLiczbaZegarow];

		// w tym bloku, czytane są dane z serwerów (oraz czas lokalny) 
		for (int numerSerwera = 0; numerSerwera < m_nLiczbaZegarow; numerSerwera++)
		{
			if(zrodloDanych[numerSerwera].compareTo("czas lokalny") != 0)
			{
				try 
				{
					Socket gniazdko = new Socket(zrodloDanych[numerSerwera] ,13 );
					DataInputStream dis = new DataInputStream(gniazdko.getInputStream());	
					daty[numerSerwera]= new Date(dis.readLine());
					gniazdko.close();
				} 
				catch(IOException ioe)
				{
					zrodloDanych[numerSerwera] = 
						"blad przy polaczeniu z "+zrodloDanych[numerSerwera];
					daty[numerSerwera] = new Date(97,01,02,11,59,59);
				}
				catch (Exception e)
				{
					zrodloDanych[numerSerwera] = 
						"blad dla "+zrodloDanych[numerSerwera];
					daty[numerSerwera] = new Date(97,01,02,11,59,59);
				}
				finally
				{
					// inicjalizacja wątków rysujących zegary i dodanie 
					// zegara do panelu
					inicjujZegar(numerSerwera, panel);
				}
			}
			else
			{   // inicjalizacja pozostałych zegarów
				daty[numerSerwera] = new Date();			
				inicjujZegar(numerSerwera, panel);
			}
		}
		add("North",panel);
		Button wyslanieWiadomosci = 
				new Button("Autor: Artur Tyloch  (arturt@friko.onet.pl)");
		add("Center",wyslanieWiadomosci);
		wyslanieWiadomosci.addActionListener(new ActionListener() 
			{
				public void actionPerformed(ActionEvent evt) 
				{
					try
					{	
						getAppletContext().showDocument(
								new URL("mailto:arturt@friko.onet.pl"),"_self");
					}
					catch(Exception e){}
				}
			}
			);
	}
	Panel zegaryGridLayoutPanel()
	{
		Panel tmpPanel = new Panel();
		tmpPanel.setLayout(new GridLayout(1,m_nLiczbaZegarow));
		return tmpPanel;
	}

	// w metodzie tej tworzymy nowy wątek dla każdego zegara 
	// i dodajemy go do panelu
	void inicjujZegar(int x, Panel zegarPanel) 
	{
		zegary[x]=new WatekZegara(daty[x], true, zrodloDanych[x]);		
		zegary[x].resize(size().width,(int)(size().height/1.2));
		zegarPanel.add(zegary[x]);
		watki[x]=new Thread(zegary[x]);
	}
	public void start()
	{
		// uruchomienie wszystkich wątków zegarów
		for (int x = 0; x < m_nLiczbaZegarow; x++)
			watki[x].start();
		// utworzenie wątku głównego, używanego do monitorowania
		// stanu zegarów 
		watekGlowny= new Thread (this);
		watekGlowny.start();
	}
	public void stop()
	{	
		watekGlowny.stop();	
	}
	public void run()
	{	
		// wykonanie pętli do czasu aż wszystkie zegary zakończą 
		// swoje działanie 
		while(Thread.activeCount() > m_nLiczbaWatkowNaStarcie+2)
		{
			try
			{	
				watekGlowny.sleep(10);	
				repaint();
			}  
			catch (InterruptedException e)
			{	
				System.out.println("watekGlowny was interrupted");	
			}
		}
		stop();	
		destroy();
	}
}

W klasie WatekZegara zdefiniowano metody odpowiedzialne za rysowanie pojedynczego zegara:

import java.util.*;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Color;

public class WatekZegara extends java.awt.Canvas implements Runnable 
{
	// współrzedne rysowanych wskazówek
	private int m_nOstatnieXS=0, m_nOstatnieYS=0, m_nOstatnieXM=0, m_nOstatnieYM=0, m_nOstatnieXH=0, m_nOstatnieYH=0;
	// ostatnio wyświetlany czas i data
	private String m_OstatniaData = null;
	// różnica pomiędzy czasem lokalnym a czasem na serwerze
	private long m_lRoznica = 0;
	// pole danych określające czy wyświetlamy czas lokalny, 
	// czy czas serwera
	private boolean m_bZdalne;
	private String m_informacja;

	WatekZegara(Date d, boolean Zdalne, String info)
	{
		Date localD = new Date();
		if ( localD != d) 
			m_lRoznica = localD.getTime() - d.getTime();
		m_OstatniaData = d.toLocaleString();
		m_bZdalne = Zdalne;
		m_informacja = info;
		
		m_nOstatnieXS=70;
		m_nOstatnieYS=m_nOstatnieXS;
		m_nOstatnieXM=m_nOstatnieXS;
		m_nOstatnieYM=m_nOstatnieXS;
		m_nOstatnieXH=m_nOstatnieXS;
		m_nOstatnieYH=m_nOstatnieXS;
	}
	
	public void run()
	{
		while (true) 
		{
			repaint();
			// uśpienie wątku, aby umożliwić przerysowania apletu
			try 
			{
				Thread.currentThread().sleep(1000);
			} 
			catch (InterruptedException e){ }
		}		
	}	

	public void update(Graphics g) 
	{
		// nie ma efektu mrugania (domniemana metoda update 
		// czyści pulpit kolorem tła, a następnie rysuje aplet),
		// tu w metodzie paint stare wskazówki są zamazywane i 
		// nie ma kłopotu z mruganiem rysowanego obrazu zegarów
		paint(g);
	}

	public synchronized void paint(Graphics g) 
	{	
	
		Date dat = new Date();
		if (m_bZdalne) 
			dat = new Date(dat.getTime()-m_lRoznica); 
		rysujZegar(dat.getSeconds(), dat.getMinutes(), dat.getHours(), size().width/2, 70, dat.toLocaleString(), g);	

		g.setFont(new Font("Helvetica", Font.BOLD, 12));
		g.drawString("Czas:",20 , 165);
		g.drawString(m_informacja,20 , 185);

		g.setFont(new Font("Courier", Font.ITALIC, 10));
		g.drawString("autor: Artur Tyloch",20 , 195);

		g.draw3DRect(2,2,size().width - 4,size().height - 4,true); 
		g.drawRoundRect(5,5,size().width - 10,size().height - 10, 45, 50);
	}

	synchronized private void rysujZegar(int s, int m, int h,
					 int xcenter, int ycenter, String dzis, Graphics g)
	{	
		int xh, yh, xm, ym, xs, ys;
		xs = (int)(Math.cos(s * 3.14f/30 - 3.14f/2) * 45 + xcenter);
		ys = (int)(Math.sin(s * 3.14f/30 - 3.14f/2) * 45 + ycenter);
		xm = (int)(Math.cos(m * 3.14f/30 - 3.14f/2) * 40 + xcenter);
		ym = (int)(Math.sin(m * 3.14f/30 - 3.14f/2) * 40 + ycenter);
		xh = (int)(Math.cos((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + xcenter);
		yh = (int)(Math.sin((h*30 + m/2) * 3.14f/180 - 3.14f/2) * 30 + ycenter);
		// Rysowanie koła i liczb
  	  g.setFont(new Font("Courier", Font.BOLD, 14));
  	  g.setColor(Color.blue);
	  g.drawOval(xcenter - 50,ycenter -50 ,100,100);
	  g.setColor(Color.yellow);
	  g.fillOval(xcenter - 45,ycenter -45 ,90,90);	  

  	  g.setColor(Color.darkGray);
  	  g.drawString("9",xcenter-45,ycenter+3); 
  	  g.drawString("3",xcenter+40,ycenter+3);
  	  g.drawString("12",xcenter-5,ycenter-37);
  	  g.drawString("6",xcenter-3,ycenter+45);

  	  // wymazanie, gdy zachodzi taka potrzeba i przerysowanie
  	  g.setColor(Color.yellow/*getBackground()*/);
	  if (xs != m_nOstatnieXS || ys != m_nOstatnieYS) 
	  {
    	  g.drawLine(xcenter, ycenter, m_nOstatnieXS, m_nOstatnieYS);
		  g.setColor(getBackground());
   		  g.drawString(m_OstatniaData, 20 , ycenter + 75);
		  g.setColor(Color.yellow/*getBackground()*/);
  	  }
  	  if (xm != m_nOstatnieXM || ym != m_nOstatnieYM) 
	  {
		  g.drawLine(xcenter, ycenter-1, m_nOstatnieXM, m_nOstatnieYM);
		  g.drawLine(xcenter-1, ycenter, m_nOstatnieXM, m_nOstatnieYM); 
	  }
  	  if (xh != m_nOstatnieXH || yh != m_nOstatnieYH) 
	  {
    	  g.drawLine(xcenter, ycenter-1, m_nOstatnieXH, m_nOstatnieYH);
		  g.drawLine(xcenter-1, ycenter, m_nOstatnieXH, m_nOstatnieYH); 
	  }
  	  g.setColor(Color.darkGray);
  	  g.drawString(dzis, 20, ycenter + 75);  
  	  g.drawLine(xcenter, ycenter, xs, ys);
  	  g.setColor(Color.blue);
  	  g.drawLine(xcenter, ycenter-1, xm, ym);
  	  g.drawLine(xcenter-1, ycenter, xm, ym);
  	  g.drawLine(xcenter, ycenter-1, xh, yh);
  	  g.drawLine(xcenter-1, ycenter, xh, yh);
  	  m_nOstatnieXS=xs; m_nOstatnieYS=ys;
  	  m_nOstatnieXM=xm; m_nOstatnieYM=ym;
  	  m_nOstatnieXH=xh; m_nOstatnieYH=yh;
  	  m_OstatniaData = dzis;
  }
}

Aplet MailAplet

W aplecie MailAplet pokazano wykorzystanie protokołu SMTP (ang. Simple Mail Transfer Protocol) do wysłania pocztę e-mail z apletu pod zadany adres.

Po wczytaniu przez przeglądarkę strony WWW zawierającej ten aplet, wysyłany jest list pod adres określony przez pole danych o nazwie adresat. W polu 'Subject' e-mail'a wysyłana jest informacja o adresie IP osoby przeglądającej stronę WWW. Do pobrania adresu IP użyto metody statycznej getLocalHost klasy InetAddress.

Dokładny opis protokołu można znaleźć w dokumencie RFC821. Ogólnie, korzysta on z portu nr 25.

Opis komunikacji:

		Klient: MAIL <SPACJA> FROM:<adres nadawcy> <CRLF>
		Serwer: 250 <komunikat> 
				K: RCPT <SPACJA> TO:<adres odbiorcy> <CRLF>
				S: 250 <komunikat>
				K: DATA <CRLF>
				S: 354 <komunikat2>
				K: Jakas przykladowa tresc listu ....
				K: ... itd. itd. itd. 
				K: <CRLF>.<CRLF> 
				S: 250 <komunikat>

W programie nie interesuje się tym, co wysyła serwer, bo zakładam, ze podaje prawdziwe dane. Poniżej przedstawiono kod apletu:

import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
public class MailAplet extends Applet
{
	String m_mojGosc = null;
	String adresat = "arturt@priv.onet.pl";

	public void init()
	{
    	resize(320, 40);
		try 
		{
			System.out.println("Przygotowanie listu\n");
			Socket socket = new Socket(getCodeBase().getHost(),25);   
			//odpowiedzi serwera można sprawdzić, gdy  zadeklarujemy: 
			//DataInputStream dis = 
			//					new DataInputStream(socket.getInputStream());
			// i będziemy je interpretować 

			DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
			dos.writeBytes("MAIL FROM:<zieloni@uop.warszawa.gov.pl>\n");
			dos.writeBytes("RCPT TO:<"+adresat+">\n");
			dos.writeBytes("DATA\r\n");
			try
			{ 
				m_mojGosc=InetAddress.getLocalHost().toString();
			}
			catch (UnknownHostException e)
			{
				m_mojGosc=e.toString();				
			}
			dos.writeBytes("Subject: GOSC: "+m_mojGosc+"\n\n");
			dos.writeBytes("Dzis odwiedzil:\r\n");
			dos.writeBytes(m_mojGosc+"\r\n");
			dos.writeBytes(".\r\n.\n\n.\nQUIT");
			dos.flush();
			System.out.println("List zostal wyslany");
			socket.close();
		} 
		catch(IOException e)
		{
			System.out.println("BLAD !!!");
		}
	}
	// wyświetlenie w oknie apletu informacji o wysłaniu e-mail'a
	public void paint(Graphics g)
	{
		g.drawString("Czesc: "+m_mojGosc, 10, 20);
		g.drawString("Mail z informacja o twojej wizycie zostal" + " wyslany do: "+adresat, 10, 30);
	}
}

Zauważmy, ze można w ten sposób wysłać list jako "ktokolwiek". Wystarczy tylko w "MAIL FROM:<adres>" podać dowolny (nawet nieistniejący) adres. Tym sposobem można podszyć się pod każdego !

A oto, kilka nagłówków (pole Subject) listów jakie otrzymałem, po umieszczeniu apletu na stronie WWW "http://friko.onet.pl/po/arturt/":

GOSC: robert/195.116.127.24
GOSC: pcjura.comarch.pl/195.116.125.114
GOSC: fornax/150.254.25.38
GOSC: wks620.inform.pucp.edu.pe/200.16.7.180  
GOSC: w014-3.ppcor.org.pl/190.59.76.23
GOSC: lala.waw.ids.edu.pl/195.117.3.20
GOSC: imul-043.uni.lodz.pl/193.59.60.56
GOSC: ds3pc94.pol.lublin.pl/194.92.19.94
GOSC: quake.tx.iex.com/192.153.191.251
GOSC: ppp.sylaba.poznan.pl/150.254.152.163
GOSC: Dolar.it.com.pl/195.116.134.101
GOSC: parkds.hscom.co.kr/150.30.50.32

Na podobnych zasadach można napisać w Javie każdą usługę sieciową.

Pisanie sieciowych aplikacji w Javie jest bardzo proste. Java dostarcza pakiet java.net, w którym są zdefiniowane wszystkie niezbędne klasy do obsługi komunikacji połączeniowej (TCP) i bezpołączeniowej (UDP), zarówno dla aplikacji klienckich jak i serwerowych.