CodeGym/Blog Java/Random-PL/Tworzenie prostej aplikacji internetowej przy użyciu serw...
John Squirrels
Poziom 41
San Francisco

Tworzenie prostej aplikacji internetowej przy użyciu serwletów i stron JSP (część 2)

Opublikowano w grupie Random-PL
Tworzenie prostej aplikacji internetowej przy użyciu serwletów i stron JSP (część 1) Wiedza wymagana do zrozumienia artykułu: Masz już mniej więcej rozeznanie w Javie Core i chciałbyś przyjrzeć się technologiom JavaEE i programowaniu WWW. Najbardziej sensowne byłoby, gdybyś obecnie studiował zadanie Java Collections, które dotyczy tematów zbliżonych do tego artykułu.
Tworzenie prostej aplikacji internetowej z wykorzystaniem serwletów i stron JSP (część 2) - 1

Tworzenie podmiotów

W pakiecie encji utworzymy Userklasę, która ma dwie prywatne zmienne łańcuchowe: name i password . toString()Utwórz konstruktory (domyślny i taki, który przyjmuje obie wartości) i pobierające/ustawiające, i na wszelki wypadek zastąp metodę wraz z metodami equals()i hashCode(). Innymi słowy, zrobimy wszystko, co szanujący się programista Java robi podczas tworzenia klasy.
public class User {
    private String name;
    private String password;

    public User() {
    }

    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (name != null ? !name.equals(user.name) : user.name != null) return false;
        return password != null ? password.equals(user.password) : user.password == null;

    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (password != null ? password.hashCode() : 0);
        return result;
    }
}
Teraz możemy przystąpić do tworzenia listy użytkowników. Dodamy do niego użytkowników i pobierzemy z niego użytkowników, aby ich wyświetlić. Mamy jednak jeden problem. Nie tworzymy naszych obiektów serwletów. Tomcat robi to za nas. Metody, które w nich nadpisujemy, są już dla nas zdefiniowane i nie możemy ich parametryzować. Jak w takim razie stworzyć wspólną listę, która będzie widoczna w obu naszych serwletach? Gdybyśmy po prostu utworzyli obiekt listy w każdym aplecie, dodalibyśmy użytkowników do jednej listy, ale wyświetlali użytkowników z innej w ListServlecie. Potrzebujemy więc obiektu, który jest wspólny dla obu serwletów. Ogólnie rzecz biorąc, potrzebujemy obiektu, który jest wspólny dla wszystkich klas w naszym programie: jeden obiekt dla całego programu. Mam nadzieję, że słyszałeś coś o wzorcach projektowych. Dla niektórych osób może to być pierwsza prawdziwa potrzeba zastosowania wzorca Singletona w ich programie. Możesz zaszaleć i wykręcić trochę słodkiego Singletona z podwójnymi kontrolami i synchronizacją (tak, nasza aplikacja jest wielowątkowa, ponieważ serwlety Tomcata działają na osobnych wątkach). Ale zamierzam użyć techniki wczesnej inicjalizacji, ponieważ jest ona całkowicie odpowiednia dla naszych celów tutaj.

Tworzenie modelu

Utwórz klasę (i zaimplementuj wzorzec Singleton ) w pakiecie modelu i nazwij ją czymś niezwykłym. Na przykład model . Stworzymy prywatną listę użytkowników w naszej klasie i zaimplementujemy dwie metody: jedną, aby dodać użytkownika, a drugą, aby zwrócić listę ciągów znaków (nazw użytkowników). Ponieważ nasz obiekt użytkownika składa się z nazwy użytkownika i hasła, a nie chcemy ujawniać haseł użytkowników, będziemy mieli tylko listę nazw.
public class Model {
    private static Model instance = new Model();

    private List<User> model;

    public static Model getInstance() {
        return instance;
    }

    private Model() {
        model = new ArrayList<>();
    }

    public void add(User user) {
        model.add(user);
    }

    public List<String> list() {
        return model.stream()
                .map(User::getName)
                .collect(Collectors.toList());
    }
}

Trochę o MVC

Ponieważ słyszałeś już o singleton , prawdopodobnie słyszałeś o innym wzorcu projektowym model-view-controller (MVC). Jego celem jest oddzielenie logiki biznesowej od widoku. Oznacza to oddzielenie kodu, który określa, co należy zrobić, od kodu, który określa sposób wyświetlania rzeczy. Widok odpowiada za sposób prezentacji danych . W naszym przypadku widokami są nasze strony JSP . Właśnie dlatego umieściłem je w folderze o nazwie views . Model to dane, z którymi program faktycznie pracuje. W naszym przypadku są to użytkownicy (lista użytkowników). Łącznikiem między nimi są kontrolery. Pobierają dane z modelu i przekazują je do widoków (lub pobierają dane z Tomcat, przetworzyć i przekazać do modelu). Definiujesz w nich swoją logikę biznesową (co program ma robić), a nie w modelu czy widoku. W ten sposób każda część zajmuje się własnym biznesem:
  • model przechowuje dane;
  • widoki renderują piękne reprezentacje danych;
  • administratorzy zajmują się przetwarzaniem danych.
Dzięki temu program jest dość prosty i łatwy w utrzymaniu, zamiast monstrualnej sterty całego kodu w jednej klasie. MVC nie tylko nadaje się do programowania stron internetowych, ale jest używany szczególnie często w tym obszarze (prawie zawsze). W naszym przypadku serwlety będą pełnić rolę kontrolerów. Jest to bardzo powierzchowny i zwięzły opis wzorca, ale MVC nie jest głównym tematem tego artykułu. Jeśli ktoś chce dowiedzieć się więcej, Google jest twoim przyjacielem! Utwórz formularz dodawania użytkownika. Dodaj formularz do add.jsp . Powinien składać się z dwóch pól tekstowych (jedno zwykłe, drugie na hasło) oraz przycisku wysyłania danych na serwer.
<form method="post">
    <label>Name:
        <input type="text" name="name"><br />
    </label>

    <label>Password:
        <input type="password" name="pass"><br />
    </label>
    <button type="submit">Submit</button>
</form>
Tutaj formularz ma atrybut metody z wartością post . Oznacza to, że dane z tego formularza trafią na serwer jako żądanie POST . Atrybut akcji nie jest określony, co oznacza, że ​​żądanie zostanie wysłane na ten sam adres, z którego weszliśmy na tę stronę ( /add ). Tak więc, po otrzymaniu żądania GET , nasz aplet związany z tym adresem zwraca stronę JSP z formularzem dodawania użytkownika. A jeśli otrzyma żądanie POST , to wiemy, że formularz wysłał tutaj swoje dane (które wyodrębniamy z obiektu żądania wdoPost()metoda, proces i przekazanie do modelu w celu zapisania). Warto zauważyć, że pola wejściowe mają parametr o nazwie name (dla nazw użytkowników lub pass dla haseł). To bardzo ważny punkt. Tak więc, aby otrzymać te dane (nazwę użytkownika i hasło, które zostaną wprowadzone) z żądania (wewnątrz serwletu), użyjemy tych pól nazwy i hasła . Ale o tym później. Mój przycisk do wysyłania danych został ponownie wykonany jako przycisk , a nie jako pole wyjściowe, jak to jest w zwyczaju. Nie wiem, jak powszechnie przyjęte jest to podejście, ale działa dla mnie (przeglądarka Chrome).

Serwletowa obsługa żądań POST

Wróćmy do AddServletu . Przypominam, że aby umożliwić naszemu serwletowi „łapanie” żądań GET , nadpisaliśmy doGet()metodę w klasie HttpServlet . Aby nauczyć nasz aplet również przechwytywania żądań POST , musimy również przesłonić doPost()metodę. Tomcat przekazuje mu podobne obiekty żądania i odpowiedzi, z którymi będziemy pracować. Aby rozpocząć, wyodrębnij nazwę żądania i przekaż parametry przesłane przez formularz (jeśli podałeś inne nazwy w formularzu, użyj tych nazw). Następnie utwórz obiekt użytkownika, korzystając z otrzymanych danych. Następnie pobieramy obiekt modelu i dodajemy utworzonego użytkownika do modelu.
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String name = req.getParameter("name");
    String password = req.getParameter("pass");
    User user = new User(name, password);
    Model model = Model.getInstance();
    model.add(user);
}

Przekazywanie danych do widoku

Przejdźmy do ListServlet . Metoda doGet()jest już zaimplementowana. Po prostu przekazuje kontrolę do widoku ( list.jsp ). Jeśli jeszcze tego nie masz, utwórz to analogicznie do metody w AddServlet . Teraz dobrze byłoby pobrać listę nazw użytkowników z modelu i przekazać je do widoku, który je otrzyma i pięknie je wyświetli. Aby to zrobić, ponownie użyjemy obiektu żądania, który otrzymaliśmy od Tomcat . Możemy dodać atrybut do tego obiektu, nadając mu jakąś nazwę. W rzeczywistości możemy dodać obiekt, który chcemy przekazać do widoku. Dzięki temu, że przekazując sterowanie z serwletu do widoku przekazujemy widokowi te same obiekty żądania i odpowiedzi, które otrzymał serwlet, możemy dodać naszą listę nazw do obiektu żądania, a następnie pobrać naszą listę nazw użytkowników z żądania obiekt w widoku. Skończyliśmy z klasą ListServlet , więc przedstawię tutaj kod całej klasy:
package app.servlets;

import app.model.Model;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

public class ListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Model model = Model.getInstance();
        List<String> names = model.list();
        req.setAttribute("userNames", names);

        RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/list.jsp");
        requestDispatcher.forward(req, resp);
    }
}

Uruchamianie kodu Java w plikach JSP

Czas przyjrzeć się list.jsp . Zostanie wykonany tylko wtedy, gdy ListServlet przekaże mu kontrolę. Ponadto przygotowaliśmy już listę nazw użytkowników z modelu w serwlecie i przekazaliśmy ją tutaj w obiekcie żądania. Ponieważ mamy listę nazwisk, możemy ją iterować za pomocą forpętli i wyświetlać każdą nazwę. Jak powiedziałem wcześniej, pliki JSP mogą wykonywać kod Java (co odróżnia je od statycznych stron HTML). Aby wykonać jakiś kod, wszystko, co musimy zrobić, to umieścić następującą konstrukcję w odpowiednim miejscu:
<!-- html code -->
<%
    // Java code
%>
<!-- html code -->
W ramach tej konstrukcji uzyskujemy dostęp do kilku zmiennych:
  • request — nasz obiekt request, który przekazaliśmy z serwletu, gdzie nazywał się po prostu req ;
  • response — obiekt odpowiedzi (nazywany w aplecie resp );
  • out — obiekt JspWriter (który dziedziczy zwykły Writer ), którego możemy użyć do „zapisania” czegoś bezpośrednio na stronie HTML . Instrukcja out.println("Witaj, świecie!") jest bardzo podobna do instrukcji System.out.println("Witaj, świecie!") , ale nie należy ich mylić!
  • out.println() „zapisuje” do strony HTML , podczas gdy System.out.println zapisuje do systemowego strumienia wyjściowego . Jeśli wywołasz System.out.println() w sekcji JSP z kodem Java , zobaczysz wyniki w konsoli Tomcat , ale nie na stronie.
Możesz szukać innych obiektów dostępnych na stronie JSP tutaj . Możemy użyć obiektu żądania , aby uzyskać listę nazw przekazanych z serwletu (dodaliśmy odpowiedni atrybut do tego obiektu), a następnie użyć obiektu out do wyświetlenia tych nazw. Wyświetlmy je (na razie jako prostą listę HTML):
<ul>
    <%
        List<String> names = (List<String>) request.getAttribute("userNames");

        if (names != null && !names.isEmpty()) {
            for (String s : names) {
                out.println("<li>" + s + "</li>");
            }
        }
    %>
</ul>
Jeśli musimy wyświetlać listę tylko wtedy, gdy są użytkownicy, a w inny sposób wyświetlać ostrzeżenie, że nie ma jeszcze żadnych użytkowników, możemy trochę przepisać tę sekcję:
<%
    List<String> names = (List<String>) request.getAttribute("userNames");

    if (names != null && !names.isEmpty()) {
        out.println("<ui>");
        for (String s : names) {
            out.println("<li>" + s + "</li>");
        }
        out.println("</ui>");
    } else out.println("<p>There are no users yet!</p>");
%>
Teraz, gdy wiemy, jak przekazywać dane z serwletów do widoków, możemy ulepszyć nasz AddServlet , aby wyświetlał powiadomienie o pomyślnym dodaniu użytkownika. W tym celu w doPost()metodzie po dodaniu nowego użytkownika do modelu możemy dodać tego użytkownika do atrybutów obiektu req i przekazać kontrolę z powrotem do widoku ( add.jsp ). A teraz dodamy do niego sekcję z kodem Java, gdzie sprawdzimy, czy żądanie ma taki atrybut, a jeśli tak — to wyświetlimy komunikat, że użytkownik został pomyślnie dodany. Po tych zmianach pełny kod AddServlet będzie wyglądał mniej więcej tak:
package app.servlets;

import app.entities.User;
import app.model.Model;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class AddServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("views/add.jsp");
        requestDispatcher.forward(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String password = req.getParameter("pass");
        User user = new User(name, password);
        Model model = Model.getInstance();
        model.add(user);

        req.setAttribute("userName", name);
        doGet(req, resp);
    }
}
Tutaj na końcu metody doPost()tworzymy atrybut z nazwą użytkownika, który został dodany do modelu, a następnie wywołujemy metodę doGet(), do której przekazujemy aktualne żądanie i odpowiedź. Metoda doGet()przekazuje teraz kontrolę do widoku, który również odbiera obiekt żądania z nazwą dodanego użytkownika dołączoną jako atrybut. Pozostało nam tylko naprawić add.jsp , aby wyświetlał powiadomienie, jeśli nie ma takiego atrybutu. Oto ostateczna wersja add.jsp :
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Add new user</title>
    </head>

    <body>
        <div>
            <h1>Super app!</h1>
        </div>

        <div>
            <%
                if (request.getAttribute("userName") != null) {
                    out.println("<p>User '" + request.getAttribute("userName") + "' added!</p>");
                }
            %>
            <div>
                <div>
                    <h2>Add user</h2>
                </div>

                <form method="post">
                    <label>Name:
                        <input type="text" name="name"><br />
                    </label>
                    <label>Password:
                        <input type="password" name="pass"><br />
                    </label>
                    <button type="submit">Submit</button>
                </form>
            </div>
        </div>

        <div>
            <button onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>
Ciało strony składa się z następujących elementów:
  • div z nagłówkiem;
  • kontener div na zawartość, który obejmuje sprawdzenie, czy istnieje atrybut z nazwą użytkownika;
  • div z formularzem dodawania użytkownika;
  • a na dole stopka z przyciskiem powrotu do strony głównej.
Może się to wydawać zbyt dużą liczbą elementów div, ale użyjemy ich później, gdy dodamy style. Oto ostateczna wersja list.jsp :
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Users</title>
    </head>

    <body>
        <div>
            <h1>Super app!</h1>
        </div>

        <div>
            <div>
                <div>
                    <h2>Users</h2>
                </div>
                <%
                    List<String> names = (List<String>) request.getAttribute("userNames");

                    if (names != null && !names.isEmpty()) {
                        out.println("<ui>");
                        for (String s : names) {
                            out.println("<li>" + s + "</li>");
                        }
                        out.println("</ui>");
                    } else out.println("<p>There are no users yet!</p>");
                %>
            </div>
        </div>

        <div>
            <button onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>
Mamy więc w pełni działającą aplikację internetową, która może zapisywać i dodawać użytkowników, a także wyświetlać listę ich nazwisk. Teraz tylko trzeba to ładnie udekorować... :) Tworzenie prostej aplikacji internetowej z wykorzystaniem serwletów i stron JSP (część 2) - 2

Dodawanie stylów. Użyjemy frameworka W3.CSS

W tej chwili nasza aplikacja działa, ale wygląda absolutnie skandalicznie. Dodajmy więc tło, pokolorujmy tekst i przyciski, dodajmy styl do list, wyrównajmy elementy, dodajmy wcięcia i tak dalej. Ręczne pisanie stylów może zająć dużo czasu i nadwyrężyć nasze nerwy. Proponuję więc użycie frameworka W3.CSS . Ma już gotowe do użycia klasy ze stylami. Musimy tylko rozmieścić klasy CSS, których chcemy użyć, we właściwych miejscach. Aby dodać je do naszych stron, najpierw łączymy plik stylów. Można to zrobić na dwa sposoby:
  1. przejrzyj nasze strony i wstaw następujący bezpośredni link do pliku stylu w sekcji head

    <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

    Ta opcja jest odpowiednia, jeśli masz stałe połączenie z Internetem. Gdy otworzysz swoje strony na lokalnym serwerze, style zostaną pobrane z Internetu.

  2. Ale jeśli chcesz mieć wszystkie style lokalnie i nie polegać na połączeniu internetowym, pobierz plik stylu i umieść go gdzieś w folderze sieciowym ( np. web/styles/w3.css ). Następnie przejrzyj wszystkie nasze strony ( index.html, add.jsp, list.jsp ) i dodaj następujący link do pliku stylu w sekcji head :

    <link rel="stylesheet" href="styles/w3.css">

    Następnie przejrzyj tagi i dodaj style, które lubisz. Nie będę się nad tym szczegółowo rozwodził. Zamiast tego po prostu udostępnię gotowe do użycia wersje trzech moich plików z klasami w stylu rastrowym.

indeks.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Super app!</title>
        <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    </head>

    <body class="w3-light-grey">
        <div class="w3-container w3-blue-grey w3-opacity w3-right-align">
            <h1>Super app!</h1>
        </div>

        <div class="w3-container w3-center">
            <div class="w3-bar w3-padding-large w3-padding-24">
                <button class="w3-btn w3-hover-light-blue w3-round-large" onclick="location.href='/list'">List users</button>
                <button class="w3-btn w3-hover-green w3-round-large" onclick="location.href='/add'">Add user</button>
            </div>
        </div>
    </body>
</html>
dodaj.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Add new user</title>
        <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    </head>

    <body class="w3-light-grey">
        <div class="w3-container w3-blue-grey w3-opacity w3-right-align">
            <h1>Super app!</h1>
        </div>

        <div class="w3-container w3-padding">
            <%
                if (request.getAttribute("userName") != null) {
                    out.println("<div class=\"w3-panel w3-green w3-display-container w3-card-4 w3-round\">\n" +
                            "   <span onclick=\"this.parentElement.style.display='none'\"\n" +
                            "   class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-green w3-border w3-border-green w3-hover-border-grey\">×</span>\n" +
                            "   <h5>User '" + request.getAttribute("userName") + "' added!</h5>\n" +
                            "</div>");
                }
            %>
            <div class="w3-card-4">
                <div class="w3-container w3-center w3-green">
                    <h2>Add user</h2>
                </div>
                <form method="post" class="w3-selection w3-light-grey w3-padding">
                    <label>Name:
                        <input type="text" name="name" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
                    </label>
                    <label>Password:
                        <input type="password" name="pass" class="w3-input w3-animate-input w3-border w3-round-large" style="width: 30%"><br />
                    </label>
                    <button type="submit" class="w3-btn w3-green w3-round-large w3-margin-bottom">Submit</button>
                </form>
            </div>
        </div>

        <div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
            <button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>
lista.jsp
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>Users list</title>
        <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    </head>

    <body class="w3-light-grey">
        <div class="w3-container w3-blue-grey w3-opacity w3-right-align">
            <h1>Super app!</h1>
        </div>

        <div class="w3-container w3-center w3-margin-bottom w3-padding">
            <div class="w3-card-4">
                <div class="w3-container w3-light-blue">
                    <h2>Users</h2>
                </div>
                <%
                    List<String> names = (List<String>) request.getAttribute("userNames");

                    if (names != null && !names.isEmpty()) {
                        out.println("<ul class=\"w3-ul\">");
                        for (String s : names) {
                            out.println("<li class=\"w3-hover-sand\">" + s + "</li>");
                        }
                        out.println("</ul>");

                    } else out.println("<div class=\"w3-panel w3-red w3-display-container w3-card-4 w3-round\">\n"
+
                            "   <span onclick=\"this.parentElement.style.display='none'\"\n" +
                            "   class=\"w3-button w3-margin-right w3-display-right w3-round-large w3-hover-red w3-border w3-border-red w3-hover-border-grey\">×</span>\n" +
                            "   <h5>There are no users yet!</h5>\n" +
                            "</div>");
                %>
            </div>
        </div>

        <div class="w3-container w3-grey w3-opacity w3-right-align w3-padding">
            <button class="w3-btn w3-round-large" onclick="location.href='/'">Back to main</button>
        </div>
    </body>
</html>
I to wszystko. :) Jeśli nadal masz jakieś pytania lub komentarze, lub jeśli coś nie działa, zostaw komentarz. I dołączę kilka zrzutów ekranu, jak to wszystko się potoczyło.
Tworzenie prostej aplikacji internetowej z wykorzystaniem serwletów i stron JSP (część 2) - 3
Tworzenie prostej aplikacji internetowej z wykorzystaniem serwletów i stron JSP (część 2) - 4
Tworzenie prostej aplikacji internetowej z wykorzystaniem serwletów i stron JSP (część 2) - 5
I na koniec , jeśli chcesz poćwiczyć z tym projektem, możesz spróbować następujących rzeczy:
  • utwórz serwlet i JSP, aby usunąć użytkownika i dodaj kolejną parę, aby edytować istniejącego użytkownika. Rezultatem będzie oryginalna aplikacja internetowa CRUD zbudowana przy użyciu serwletów. ;)
  • zastąp Listę bazą danych, aby dodani użytkownicy nie znikali po restarcie serwera. :)
Powodzenia!
Komentarze
  • Popularne
  • Najnowsze
  • Najstarsze
Musisz się zalogować, aby dodać komentarz
Ta strona nie ma jeszcze żadnych komentarzy