sobota, 22 października 2011

Rekurencyjne listowanie plików

Program napisany na szybko z lenistwa. Przechodzi rekurencyjnie przez katalogi i wypisuje do pliku tekstowego pliki o podanych rozszerzeniach (lub wszystkie jeśli nie podano rozszerzeń).

Jeśli pojawią się problemy ze znakiem końca linii trzeba podać jako argument jaki znak ma być użyty. Dla unix'a to będzie LF, dla windowsa CRLF a dla mac'a CR. standardowo użyty jest LF.

Czyli na przykład:
java -jar Listowanie.jar CRLF


Pobierz

Program testowany tylko na Win7 x64.


środa, 31 sierpnia 2011

Dziennik Ustaw v0.1

Wstępna wersja aplikacji pobierającej informacje o najnowszych numerach dziennika ustaw. Wyświetla numery i daty wydania ostatnich 10 numerów dziennika ustaw. Informacje aktualizowane są co 5 minut.

DOWNLOAD

Wymagana Java 7

Zabawa z regexem - wyrażenia regularne

Śledzę czasem Dziennik Ustaw czekając na interesujące mnie rozporządzenie. Jednak Rządowe Centrum Legislacji nie udostępnia kanału RSS (przynajmniej o takowym nie wiem).

Prostym rozwiązaniem tego problemu jest napisanie aplikacji co kilka minut sprawdzającej czy nie pojawił się nowy DU. Do tego celu użyjemy Regex'a czyli wyrażeń regularnych.

Obsługa wyrażeń regularnych w javie składa się z klas Pattern oraz Matcher. Klasa Pattern, metodą static Pattern compile(String regex), tworzy wzorzec na podstawie podanego w argumencie wyrażenia regularnego. Instancję klasy Matcher tworzymy przy użyciu metody klasy Pattern Matcher matcher(CharSequence input).

Następnie używamy metod boolean find() oraz String group() klasy Matcher aby, odpowiednio, odnaleźć następną pasującą subsekwencję oraz uzyskać ją w formie String'a.

Na przykładzie strony http://www.dziennikustaw.gov.pl/ wypisujemy na ekran numery oraz daty ukazania się ostatnich 10 numerów dziennka ustaw.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main
{
    public static void main(String[] args) throws MalformedURLException, IOException
    {
        /* POBRANIE ŹRÓDŁA STRONY*/
        URL url = new URL("http://dziennikustaw.gov.pl/");
        BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
        
        StringBuilder buff = new StringBuilder();
        
        String line;
        while((line = in.readLine())!=null)
        {
            buff.append(line);
            buff.append("\n");
        }
        String str = buff.toString();
        
        in.close();
        
        /* PARSOWANIE ŹRÓDŁA */
        Pattern pattNumer = Pattern.compile("<td class=\"c\">([0-9]{1,3})</td>");
        Matcher matchNumer = pattNumer.matcher(str);
        
        Pattern pattData = Pattern.compile("<td class=\"c\">([0-9]{4}-[0-9]{2}-[0-9]{2}) ([0-9]{2}:[0-9]{2}:[0-9]{2})<br />"); //br bez spacji
        Matcher matchData = pattData.matcher(str);
        
        int i=0;
        
        while(matchNumer.find() && matchData.find() && i<10)
        {
            System.out.println(matchNumer.group(1)+" "+matchData.group(1)+" "+matchData.group(2));
            
            i++;
        }
    }
}
W wyniku działania programu otrzymamy:
run:
179 2011-08-30 16:56:35
178 2011-08-29 18:44:54
177 2011-08-26 19:57:05
176 2011-08-26 19:43:00
175 2011-08-25 18:21:44
174 2011-08-23 17:42:06
173 2011-08-22 17:21:39
172 2011-08-19 17:10:41
171 2011-08-18 18:36:57
170 2011-08-18 18:31:47
BUILD SUCCESSFUL (total time: 0 seconds)
A więc mamy wszystkie potrzebne dane. Jest to dobry zalążek do pisania większej aplikacji.


EDIT: Poprawione, wydobywanie danych przy użyciu group() zamiast replaceAll().

czwartek, 7 lipca 2011

MySQL - Połączenie JAVA (JDBC) i PHP (mysqli)

Połączenie z bazą MySQL przy użyciu JDBC (java) i mysqli (php). Oba programy wypisują na standardowe wyjście ciąg "Hello World" po wcześniejszym zapisaniu go i odczytaniu z bazy danych.

JAVA
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

public class Main
{
    public static void main(String[] args)
    {
        try
        {
            Connection sql = DriverManager.getConnection("jdbc:mysql://localhost:3306", "user", "pass"); //ustanowienie połączenia
            Statement stat = sql.createStatement(); //utworzenie obiektu statement
            
            stat.execute("USE db"); //wybór bazy
            stat.execute("INSERT INTO someTable (txt) VALUES ('Hello World')"); //dodanie rekordu do tabeli
            
            ResultSet rs = stat.executeQuery("SELECT * FROM someTable"); //pobranie danych z bazy
            rs.first();
            System.out.println(rs.getString("txt")); //wypisanie rekordu na standardowe wyjście
            
            stat.execute("DELETE FROM someTable"); //usunięcie danych z tabeli
            
            stat.close(); //zamknięcie strumieni
            sql.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

PHP

wtorek, 21 czerwca 2011

Socket - Połączenie klient-serwer

Dzisiaj zajmiemy się połączeniem pomiędzy klientem i serwerem przy użyciu klasy Socket. Klasa Socket pozwala na połączenie sieciowe na podanym porcie pomiędzy dwoma maszynami.

Socket (Java Platform SE 6)

Krótko o działaniu aplikacji. Po uruchomieniu serwer oczekuje na połączenie. Po połączeniu serwer wysyła do klienta komunikat "Ping" i oczekuje na odpowiedź. Jeśli klient odpowie "Pong" to serwer po 5 sekundach ponownie wysyła "Ping". W przypadku otrzymania złej odpowiedzi serwer się wyłącza.

Klient sprawdza nadchodzące komunikaty, jeśli otrzyma "Ping" to odpowiada "Pong". Dla celów testowych klient po 30 sekundach działania wyśle komunikat "Pon g" zamiast "Pong" i się wyłączy.

Linijki potrzebne do działania po stronie serwera:
/*Utworzenie na podanym porcie socketa serwera.*/
ServerSocket serverSocket = new ServerSocket(PORT);

/*Oczekiwanie na połączenie i zaakceptowanie*/
Socket clientSocket = serverSocket.accept();

/*Utworzenie strumieni wejściowego i wyjściowego*/
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

/*Wysłanie komunikatu*/
out.println(KOMUNIKAT);

/*Oczekiwanie i odebranie komunikatu*/
String input = in.readLine();

/*Zamknięcie strumieniu i socketów*/
out.close();
in.close();
clientSocket.close();
serverSocket.close();

Linijki potrzebne do działania po stronie klienta:
/*Połączenie z serwerem*/
Socket socket = new Socket(HOST, PORT);

/*Reszta analogicznie jak w serwerze*/


Kod źródłowy:
/* Server.java */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server
{
    private static final int PORT = 50000;
    static boolean flaga = true;
    
    private static ServerSocket serverSocket;
    private static Socket clientSocket;
    
    public static void main(String[] args) throws IOException
    {
        serverSocket = null;
        try
        {
            serverSocket = new ServerSocket(PORT);
        }
        catch(IOException e)
        {
            System.err.println("Could not listen on port: "+PORT);
            System.exit(1);
        }
        
        System.out.print("Wating for connection...");
        
        Thread t = new Thread(new Runnable()
        {
            public void run()
            {
                try
                {
                    while(flaga)
                    {
                        System.out.print(".");
                        Thread.sleep(1000);
                    }
                }
                catch(InterruptedException ie)
                {
                    //
                }
                
                System.out.println("\nClient connected on port "+PORT);
            }
        });
        t.start();
        
        clientSocket = null;
        try
        {
            clientSocket = serverSocket.accept();
            flaga = false;
        }
        catch(IOException e)
        {
            System.err.println("Accept failed.");
            t.interrupt();
            System.exit(1);
        }
        
        final PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
        final BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        
        t = new Thread(new Runnable()
        {
            public void run()
            {
                try
                {
                    Thread.sleep(5000);
                    
                    while(true)
                    {
                        out.println("Ping");
                        System.out.println(System.currentTimeMillis()+" Ping sent");
                        
                        String input = in.readLine();
                        
                        if(input.equals("Pong"))
                        {
                            System.out.println(System.currentTimeMillis()+" Pong received");
                        }
                        else
                        {
                            System.out.println(System.currentTimeMillis()+" Wrong answer");
                            
                            out.close();
                            in.close();
                            clientSocket.close();
                            serverSocket.close();
                            break;
                        }
                        
                        
                        Thread.sleep(5000);
                    }
                }
                catch(Exception e)
                {
                    System.err.println(System.currentTimeMillis()+" Unexpected Error");
                }
            }
        });
        t.start();
    }
}

/* Client.java */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client
{
    private static final int PORT = 50000;
    private static final String HOST = "localhost";
    
    public static void main(String[] args) throws IOException
    {
        Socket socket = null;
        
        try
        {
            socket = new Socket(HOST, PORT);
        }
        catch(Exception e)
        {
            System.err.println("Could not connect to "+HOST+":"+PORT);
            System.exit(1);
        }
        
        final PrintWriter out = new PrintWriter(socket.getOutputStream(),true);
        final BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        
        Thread t = new Thread(new Runnable()
        {
            public void run()
            {
                long start = System.currentTimeMillis();
                
                while (true)
                {
                    try
                    {
                        String input = in.readLine();

                        if (input != null)
                        {
                            System.out.println(System.currentTimeMillis() + " Server: " + input);
                        }

                        if (input.equals("Ping"))
                        {
                            if(System.currentTimeMillis()-start>30000)
                            {
                                out.println("Pon g");
                                System.out.println(System.currentTimeMillis() + " Client: Pon g");
                                break;
                            }
                            
                            out.println("Pong");
                            System.out.println(System.currentTimeMillis() + " Client: Pong");
                        }
                    }
                    catch (IOException ioe)
                    {
                        //
                    }
                }
            }
        });
        t.start();
        
        out.close();
        in.close();
        socket.close();
    }
}

Output serwera:
Wating for connection..........
Client connected on port 50000
1308645130778 Ping sent
1308645130779 Pong received
1308645135779 Ping sent
1308645135779 Pong received
1308645140780 Ping sent
1308645140780 Pong received
1308645145780 Ping sent
1308645145780 Pong received
1308645150780 Ping sent
1308645150780 Pong received
1308645155781 Ping sent
1308645155781 Wrong answer

Output klienta:
1308645130778 Server: Ping
1308645130779 Client: Pong
1308645135779 Server: Ping
1308645135779 Client: Pong
1308645140780 Server: Ping
1308645140780 Client: Pong
1308645145780 Server: Ping
1308645145780 Client: Pong
1308645150780 Server: Ping
1308645150780 Client: Pong
1308645155781 Server: Ping
1308645155781 Client: Pon g

niedziela, 5 czerwca 2011

db4o - Prosta baza danych dla aplikacji java!

Ostatnio zastanawiałem się jak przechowywać dane pomiędzy uruchomieniami aplikacji. Szukając rozwiązań w internecie trafiłem na db4objects http://db4o.com/.

db4o jest bazą embedded przechowującą całe obiekty. Całość działa szybko i jest bardzo prosta w implementacji.

Potrzebne biblioteki pobieramy z tej strony:
http://db4o.com/DownloadNow.aspx -> db4o 8.0 for Java (zip, 40.9 mb).
Rejestracja jest opcjonalna.

Wg pliku \db4o-8.0-java\db4o-8.0\lib\readme.html wystarczy nam biblioteka db4o-8.0.184.15484-core-java5.jar ale do projektu dodajemy również db4o-8.0.184.15484-cs-java5.jar ponieważ wg API (dołączone do paczki) potrzebna nam metoda w klasie Db4o jest zdeprecjonowana.


Oto krótki przykład zastosowania:
/* Main.java */
import com.db4o.ObjectContainer;
import com.db4o.ObjectServer;
import com.db4o.cs.Db4oClientServer;
import com.db4o.query.Predicate;
import java.util.List;

public class Main
{
    static ObjectServer os;
    
    public static void main(String[] args)
    {
        os = Db4oClientServer.openServer("TestDB", 0);
        
        saveToDB();
        loadFromDB();
        
        os.close();
    }
    
    public static void saveToDB()
    {
        ObjectContainer oc = os.openClient();
        oc.store(new SomeObject());
        oc.close();
    }
    
    public static void loadFromDB()
    {
        ObjectContainer oc = os.openClient();
        
        List list = oc.query(new Predicate()
                {
                    public boolean match(SomeObject so)
                    {
                        return so.getClass().getName().equals("SomeObject");
                    }
                });
        
        for(SomeObject so : list)
        {
            System.out.println(so.x+" "+so.y);
        }
        
        oc.close();
    }
}

/* SomeObject.java */
import java.util.Random;

public class SomeObject
{
    public int x;
    public int y;
    
    public SomeObject()
    {
        Random r = new Random();
        
        x = r.nextInt(10);
        y = r.nextInt(100);
    }
}

Wynik 3-krotnego uruchomienia aplikacji, widać że dane są zachowywane:
run:
6 58
3 42
6 22
BUILD SUCCESSFUL (total time: 1 second)

Jak widać potrzebne obsługa bazy jest banalnie prosta, potrzebne linie to:

ObjectServer os = Db4oClientServer.openServer("TestDB", 0); //otworzenie serwera

ObjectContainer oc = os.openClient(); //otworzenie klienta

oc.store(new SomeObject()); //zapisanie obiektu do bazy

//pobranie obiektów SomeObject z bazy
List list = oc.query(new Predicate()
                {
                    public boolean match(SomeObject so)
                    {
                        return so.getClass().getName().equals("SomeObject");
                    }
                });

oc.close(); //zamknięcie klienta

os.close(); //zamknięcie serwera

Metoda openServer() w razie potrzeby utworzy nowy serwer pod podaną nazwą, a jeśli chcemy aby był to serwer TCP zamiast 0 podajemy numer portu pod jakim ma działać.

Pokazane tutaj zastosowanie jest najprostszym z możliwych ale baza ta jest o wiele bardziej rozbudowana.

sobota, 28 maja 2011

Java - Po prostu wątki

Często pisząc aplikację natrafiamy na sytuację w której chcemy wykonać dwie (albo i więcej) rzeczy jednocześnie. Na przykład, chcemy jednocześnie rysować JPanel, obliczać w czasie rzeczywistym nowe dane do wyświetlenia i dodatkowo obsługiwać zdarzenia użytkownika.

Dzisiaj napiszemy prostą aplikacje z zastosowaniem wielowątkowości.

Na początek przygotujmy okno z panelem i przyciskiem. Panel będzie wyświetlał koło wielkości 10x10px o środku w miejscu wskazywanym przez zmienną location. Po naciśnięciu przycisku koło będzie zmieniało kolor.

/* Main.java */

public class Main
{
    public static void main(String[] args)
    {
        Frame f = new Frame("Java - Multithreading");
        
        f.budujOkno();
    }
}

/* Frame.java */

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.border.LineBorder;

public class Frame extends JFrame
{
    JButton button;
    Panel p;
    
    public final static int X = 400;
    public final static int Y = 400;
    
    public Frame(String title)
    {
        super(title);
    }
    
    public void budujOkno()
    {
        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
        
        setSize(435,487);
        setLayout(null);
        setVisible(true);
        
        button = new JButton("Przycisk");
        button.setLocation(100,420);
        button.setSize(200,20);
        button.setVisible(true);
        button.addActionListener(new ActionListener()
                {
                    public void actionPerformed(ActionEvent e)
                    {
                        p.changeColor();
                    }
                });
        add(button);
        
        p = new Panel();
        p.setLocation(10,10);
        p.setSize(X,Y);
        p.setVisible(true);
        p.setBorder(new LineBorder(Color.BLACK));
        p.setBackground(Color.white);
        add(p);
    }
}

/* Panel.java */

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;

public class Panel extends JPanel
{
    public int[] location = null;
    
    private int color = 0;
    private Color[] colors = {Color.red, Color.yellow, Color.blue, Color.green};
    
    public Panel()
    {
        super(null);
    }
    
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        
        Graphics2D g2d = (Graphics2D)g;
        
        if(location != null)
        {
            g2d.setColor(colors[color]);
            g2d.fillOval(location[0]-5, location[1]-5, 10, 10);
            
            g2d.setColor(Color.black);
            g2d.drawOval(location[0]-5, location[1]-5, 10, 10);
        }
    }
    
    public void changeColor()
    {
        if(color+1 < colors.length)
            color++;
        else
            color = 0;
    }
    
    public void setLoc(int x, int y)
    {
        if(location == null)
            location = new int[2];
        
        location[0] = x;
        location[1] = y;
    }
}
Wszystko ok, program się uruchamia, ale jest coś nie tak. Koło się nie wyświetla bo zmienna location jest null'em. Trzeba obliczyć pozycję koła. Do tego celu użyjemy pierwszego dziś wątku. Tworzymy klasę anonimową implementującą interface Runnable. Chcemy aby obliczenia wykonywały się do zamknięcia programu więc w metodzie run znajdzie się nieskończona pętla. Do klasy Frame dodajemy import klasy Random potrzebnej do wylosowania pozycji początkowej, zmienną dla wątku obliczającego, przerwanie wątku w przypadku zamknięcia aplikacji oraz utworzenie nowego wątku.
/* Frame.java */
import java.util.Random;
...
    Thread obliczenia;
...
            if(obliczenia != null)
                obliczenia.interrupt();
...
        obliczenia = new Thread(new Runnable()
        {
            public void run()
            {
                int kX = 1;
                int kY = 1;
                
                int x = (new Random().nextInt((Frame.X - 10) / 10) + 10) * 10;
                int y = (new Random().nextInt((Frame.Y - 10) / 10) + 10) * 10;

                try
                {
                    while (true)
                    {
                        if (kX * 10 + x >= Frame.X)
                        {
                            x = Frame.X;
                            kX *= -1;
                        }
                        else if(kX*10 + x <= 0)
                        {
                            x = 0;
                            kX *= -1;
                        }
                        else
                        {
                            x = kX * 10 + x;
                        }

                        if (kY * 10 + y >= Frame.Y)
                        {
                            y = Frame.Y;
                            kY *= -1;
                        }
                        else if(kY*10 + y <= 0)
                        {
                            y = 0;
                            kY *= -1;
                        }
                        else
                        {
                            y = kY * 10 + y;
                        }

                        p.setLoc(x, y);

                        Thread.sleep(10);
                    }
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });
        obliczenia.start();
...
W tej chwili pozycja jest już obliczana 100 razy na sekundę, natomiast nadal się nie wyświetla. Z tego powodu potrzebny jest wątek odświeżający panel, powiedzmy, 50 razy na sekundę. Ponownie wykorzystamy do tego celu klasę anonimową. Oto ostateczna wygląd klasy Frame:
/* Frame.java */

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.border.LineBorder;

public class Frame extends JFrame
{
    JButton button;
    Panel p;
    
    public final static int X = 400;
    public final static int Y = 400;
    
    Thread obliczenia;
    Thread obraz;
    
    public Frame(String title)
    {
        super(title);
    }
    
    public void budujOkno()
    {
        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                if(obliczenia != null)
                    obliczenia.interrupt();
                
                if(obraz != null)
                    obraz.interrupt();
                
                System.exit(0);
            }
        });
        
        setSize(435,487);
        setLayout(null);
        setVisible(true);
        
        button = new JButton("Przycisk");
        button.setLocation(100,420);
        button.setSize(200,20);
        button.setVisible(true);
        button.addActionListener(new ActionListener()
                {
                    public void actionPerformed(ActionEvent e)
                    {
                        p.changeColor();
                    }
                });
        add(button);
        
        p = new Panel();
        p.setLocation(10,10);
        p.setSize(X,Y);
        p.setVisible(true);
        p.setBorder(new LineBorder(Color.BLACK));
        p.setBackground(Color.white);
        add(p);
        
        obliczenia = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                int kX = 1;
                int kY = 1;
                
                int x = (new Random().nextInt((Frame.X - 10) / 10) + 10) * 10;
                int y = (new Random().nextInt((Frame.Y - 10) / 10) + 10) * 10;

                try
                {
                    while (true)
                    {
                        if (kX * 10 + x >= Frame.X)
                        {
                            x = Frame.X;
                            kX *= -1;
                        }
                        else if(kX*10 + x <= 0)
                        {
                            x = 0;
                            kX *= -1;
                        }
                        else
                        {
                            x = kX * 10 + x;
                        }

                        if (kY * 10 + y >= Frame.Y)
                        {
                            y = Frame.Y;
                            kY *= -1;
                        }
                        else if(kY*10 + y <= 0)
                        {
                            y = 0;
                            kY *= -1;
                        }
                        else
                        {
                            y = kY * 10 + y;
                        }

                        p.setLoc(x, y);

                        Thread.sleep(10);
                    }
                }
                catch (InterruptedException e)
                {
                    //
                }
            }
        });
        obliczenia.start();
        
        obraz = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    while(true)
                    {
                        p.repaint();
                        Thread.sleep(20);
                    }
                }
                catch(InterruptedException e)
                {
                    //
                }
            }
        });
        obraz.start();
    }
}
A oto efekt końcowy: