Commit 33361eb0 by Patryk Czarnik

Dodatkowe gotowe projekty

parent 83a46550
package sklep.rest;
import java.util.List;
import jakarta.enterprise.context.RequestScoped;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import sklep.db.CustomerDAO;
import sklep.db.DBConnection;
import sklep.db.DBException;
import sklep.db.RecordNotFound;
import sklep.model.Customer;
@RequestScoped
@Path("/customers")
@Produces({ "application/xml", "application/json" })
@Consumes({ "application/xml", "application/json" })
public class RCustomer {
@POST
public Response save(final Customer customer) throws DBException {
try(DBConnection db = DBConnection.open()) {
CustomerDAO customerDAO = db.customerDAO();
customerDAO.save(customer);
db.commit();
return Response.created(UriBuilder.fromResource(RCustomer.class).path(String.valueOf(customer.getEmail())).build()).build();
}
}
@GET
@Path("/{id}")
public Customer findById(@PathParam("id") final String email) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
CustomerDAO customerDAO = db.customerDAO();
return customerDAO.findByEmail(email);
}
}
@GET
public List<Customer> listAll() throws DBException {
try(DBConnection db = DBConnection.open()) {
CustomerDAO customerDAO = db.customerDAO();
return customerDAO.readAll();
}
}
@PUT
@Path("/{id}")
public Response update(@PathParam("id") String email, final Customer customer) throws DBException {
try(DBConnection db = DBConnection.open()) {
CustomerDAO customerDAO = db.customerDAO();
customer.setEmail(email);
customerDAO.save(customer);
db.commit();
}
return Response.noContent().build();
}
@DELETE
@Path("/{id}")
public Response deleteById(@PathParam("id") String email) throws DBException {
try(DBConnection db = DBConnection.open()) {
CustomerDAO customerDAO = db.customerDAO();
customerDAO.delete(email);
db.commit();
}
return Response.noContent().build();
}
}
package sklep.rest;
import java.net.URI;
import java.util.List;
import jakarta.enterprise.context.RequestScoped;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.UriBuilder;
import sklep.db.CustomerDAO;
import sklep.db.DBConnection;
import sklep.db.DBException;
import sklep.db.OrderDAO;
import sklep.db.RecordNotFound;
import sklep.model.Customer;
import sklep.model.Order;
@RequestScoped
@Path("/orders")
@Produces({ "application/xml", "application/json" })
@Consumes({ "application/xml", "application/json" })
public class ROrder {
@GET
public List<Order> listAll() throws DBException {
try(DBConnection db = DBConnection.open()) {
OrderDAO orderDAO = db.orderDAO();
return orderDAO.readAll();
}
}
@GET
@Path("/{id:[0-9][0-9]*}")
public Response findById(@PathParam("id") final Integer id) {
// Klasa Response pozwala nam z pełną precyzją przygotować odpowiedź, która ma zostać odesłana klientowi.
// W przypadku pozytywnym (ok) zostanie odesłany obiekt przetłumaczony na XML lub JSON, a kod wynikowy to będzie 200.
// Ale w przypadku błędów możemy sami zdecydować co odsyłami (tutaj odpowiednie kody HTTP).
try(DBConnection db = DBConnection.open()) {
OrderDAO orderDAO = db.orderDAO();
Order order = orderDAO.findById(id);
return Response.ok(order).build();
} catch (DBException e) {
e.printStackTrace();
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
} catch (RecordNotFound e) {
return Response.status(Status.NOT_FOUND).build();
}
}
/*
// Metoda, która ma obsłużyć pobranie info o właścicielu zamówienia:
// /orders/1/customer
// W tej wersji metoda zwraca bezpośrednio dane klienta.
// Wada tego podejścia: ten sam rekord (konkretny klient) jest widoczny pod różnymi adresami URL.
@GET
@Path("/{id:[0-9][0-9]*}/customer")
public Customer getCustomer(@PathParam("id") Integer orderId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
OrderDAO orderDAO = db.orderDAO();
CustomerDAO customerDAO = db.customerDAO();
Order order = orderDAO.findById(orderId);
Customer customer = customerDAO.findByEmail(order.getCustomerEmail());
return customer;
}
}
*/
// W tej wersji w odpowiedzi na zapytanie o dane klienta, który złożył zamówienie,
// wyślemy przekierowanie pod adres tego klienta.
// To jest lepsze z punktu widzenia "dobrych praktyk REST".
@GET
@Path("/{id:[0-9][0-9]*}/customer")
public Response getCustomer(@PathParam("id") Integer orderId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
OrderDAO orderDAO = db.orderDAO();
Order order = orderDAO.findById(orderId);
URI customerURI = UriBuilder.fromResource(RCustomer.class)
.path("/{email}")
.build(order.getCustomerEmail());
return Response.seeOther(customerURI).build();
}
}
}
/target/
/.settings/
/.classpath
/.project
/*.iml
/.idea/
http://api.nbp.pl/
http://api.nbp.pl/api/exchangerates/tables/a/
http://api.nbp.pl/api/exchangerates/tables/a/?format=xml
http://api.nbp.pl/api/exchangerates/tables/a/?format=json
http://api.nbp.pl/api/exchangerates/tables/b/
http://api.nbp.pl/api/exchangerates/tables/a/2020-01-15/?format=xml
http://api.nbp.pl/api/exchangerates/tables/a/2020-01-15/?format=json
Build Tool - programik, który potrafi "zbudować projekt", czyli na podstawie źródeł zrobić wersję gotową do działania. Budowanie to m.in. kompilacja (javac), ale nie tylko.
Build Tools dla Javy:
Ant - dawno temu bardzo popularny, ale teraz już nie
Maven - najważniejsze narzędzie, do dzisiaj używane w projektach "enterprise" (serwerowych) i w ogóle...
automatyczne pobieranie zależności, czyli innych bibliotek, których chcemy użyć w naszym projekcie
Gradle - koncepcja podobna do Maven, ale bardziej nowoczesne
używane dla aplikacji Android oraz coraz częściej aplikacji serwerowych robionych w Spring
Edytory IDE znają te narzędzia i potrafią ich użyć - my nie musimy wpisywać poleceń - ale od tego, które z tych narzędzi wybierzemy, zależy struktura projektu i pliki, jakie w nim muszą być.
Maven → plik pom.xml i katalogi takie jak src/main/java...
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.alx.kjava</groupId>
<artifactId>PC06-ProjektMavenowy</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.10.0</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.json</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package biblioteki;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
public class CommonsLang {
public static void main(String[] args) {
String[] t = {null, "Ala", "Agnieszka", "Zenon", "Żaneta", "Łukasz", "Łucja", "ala", "łukasz", "", " "};
// Gdy w Javie porównujemy Stirngi (i inne obiekty), robimy to za pomocą equals.
// Gdybyśmy próbowali wywołać equals na zmiennej, która może by nullem, to doprowadzimy do błędu NPE
// for(int i = 2; i >= 0; i--) {
// if(t[i].equals("Ala")) {
// System.out.println(i + " to jest Ala");
// } else {
// System.out.println(i + " to nie jest Ala");
// }
// }
// Dlatego zalecane jest, aby zmienne porównywać z konkretnymi napisami w tej kolejności:
// "Napis".equals(zmienna)
// a nie zmienna.equals("Napis")
for(int i = 2; i >= 0; i--) {
if("Ala".equals(t[i])) {
System.out.println(i + " to jest Ala");
} else {
System.out.println(i + " to nie jest Ala");
}
}
// Jednak czasami porównujemy dwie zmienne, z których obie mogą być nullem, wtedy jest trudniej.
// Biblioteka commons-lang i klasa StringUtils pomaga w operacjach porównywania, sprawdzania czy napis jest pusty itd.
System.out.println();
// Porównania stringów, które są odporne na wartości null (nie pojawiają się błędy)
System.out.println(StringUtils.equalsIgnoreCase(t[0], t[7])); // false, bo null != ala
System.out.println(StringUtils.equalsIgnoreCase(t[1], t[7]));
System.out.println(StringUtils.equalsIgnoreCase(t[5], t[8])); // Łukasz == łukasz
System.out.println();
System.out.println(StringUtils.isEmpty(t[0])); // tak, bo jest nullem
System.out.println(StringUtils.isEmpty(t[9])); // tak, bo jest pustym stringiem
System.out.println(StringUtils.isEmpty(t[10])); // false, bo zawiera 3 spacje
System.out.println(StringUtils.isBlank(t[10]));
System.out.println(StringUtils.isBlank(t[0]));
System.out.println(StringUtils.isBlank(t[1]));
System.out.println();
// są też wersje dla wielu wartości na raz
System.out.println(StringUtils.isAllBlank(t[0], t[9], t[10]));
System.out.println(StringUtils.isAllBlank(t[0], t[8], t[10]));
// praktyczne zastosowanie: walidacja danych wprowadzonych przez użytkownika:
String imie = "Ala", nazwisko = "Kowalska", adres = " ";
if(StringUtils.isAnyBlank(imie, nazwisko, adres)) {
System.out.println("Nie podałeś danych");
} else {
// robimy coś ważnego
}
System.out.println();
for (String s : t) {
if(StringUtils.isNotEmpty(s)) {
System.out.println("|" + StringUtils.center(s, 20, '.') + "|");
}
}
System.out.println();
System.out.println(ObjectUtils.firstNonNull(t));
}
}
package biblioteki;
import java.util.Arrays;
import org.apache.commons.math3.stat.StatUtils;
public class CommonsMath {
public static void main(String[] args) {
double[] a = {2, 10, 4.44, 3.14, 0, 25.5};
System.out.println(Arrays.toString(a));
double srednia = StatUtils.mean(a);
System.out.println(srednia);
}
}
package biblioteki;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
public class CzytajPlikCSV {
public static void main(String[] args) {
File plik = new File("emps.csv");
CSVFormat format = CSVFormat.Builder.create()
.setDelimiter(';')
.setHeader().setSkipHeaderRecord(true)
.build();
try(CSVParser parser = CSVParser.parse(plik, Charset.forName("UTF-8"), format)) {
for(CSVRecord record : parser) {
// System.out.println(record);
// System.out.printf("Pracownik %s %s (%s) zarabia %s\n",
// record.get(1), record.get(2), record.get(3), record.get(4));
System.out.printf("Pracownik %s %s (%s) zarabia %s\n",
record.get("first_name"), record.get("last_name"), record.get("job_title"), record.get("salary"));
}
} catch (IOException e) {
e.printStackTrace();
};
}
}
package biblioteki;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
public class SkopiujKatalog {
// W standardowej bibliotece Javy nie ma operacji, która kopiowałaby na dysku cały katalog z plikami.
// W tym przykładzie użyjemy biblioteki Apache Commons IO, która taką operację zawiera.
public static void main(String[] args) {
try {
File zrodlo = new File("src/main/java");
File cel = new File("/home/patryk/Pulpit/zrodla");
FileUtils.copyDirectory(zrodlo, cel);
System.out.println("Gotowe");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package hello;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello Maven!");
}
}
package hello;
import java.util.stream.Stream;
public class TestLambdy {
public static void main(String[] args) {
String[] t = {"Ala", "Agnieszka", "Ola", "Żaneta"};
Stream.of(t).forEach(s -> System.out.println("Witaj " + s));
}
}
package testowanie;
public class BrakSrodkow extends Exception {
public BrakSrodkow() {
super();
}
public BrakSrodkow(String message) {
super(message);
}
}
package testowanie;
public class Funkcje {
public static long silnia(int n) {
long wynik = 1;
for(int i = 1; i <= n; i++) {
wynik *= i;
}
return wynik;
}
public static long fib(int n) {
if(n <= 1) return n;
return fib(n-2) + fib(n-1);
}
}
package testowanie;
public class Konto {
private final int numer;
private int saldo;
private String wlasciciel;
public Konto(int numer, int saldo, String wlasciciel) {
this.numer = numer;
this.saldo = saldo;
this.wlasciciel = wlasciciel;
}
public int getNumer() {
return numer;
}
public int getSaldo() {
return saldo;
}
public String getWlasciciel() {
return wlasciciel;
}
@Override
public String toString() {
return "Konto nr " + numer + ", saldo " + saldo + ", wł. " + wlasciciel;
}
public void wplata(int kwota) {
if(kwota <= 0) {
throw new IllegalArgumentException("Niedodatnia kwota w metodzie wplata");
}
this.saldo += kwota;
}
public void wyplata(int kwota) throws BrakSrodkow {
if(kwota <= 0) {
throw new IllegalArgumentException("Niedodatnia kwota w metodzie wyplata");
}
if(kwota > saldo) {
throw new BrakSrodkow("Brak środków na koncie nr " + numer);
}
this.saldo -= kwota;
}
}
package testowanie;
import javax.swing.JOptionPane;
public class Testowanie1_Interaktywnie {
// Jednym ze sposobów, aby przetestować napisane przez nas klasy, metody, funkcje
// jest uruchomić kompletną aplikację, w której tester interaktywnie sprawdza co robi program i jakie wyniki daje.
// Testowanie manualne JEST potrzebne i na pewny etapie musi wystąpić,
// ale stosowanie testownaia manualnego do sprawdzenia czy pojedyncza funkcja działa poprawnie
// jest pracochłonne i kosztowne.
// Wykrycie błędów logicznych, szczególnie obliczeniowych, wymaga dużej uwagi i łatwo takie błędy przeoczyć.
public static void main(String[] args) {
while(true) {
String input = JOptionPane.showInputDialog("Podaj liczbę");
if(input == null) break;
try {
int arg = Integer.parseInt(input);
var silnia = Funkcje.silnia(arg);
var fib = Funkcje.fib(arg);
JOptionPane.showMessageDialog(null,
String.format("silnia(%d) = %d\nfib(%d) = %d",
arg, silnia, arg, fib));
} catch (ArithmeticException | NumberFormatException e) {
JOptionPane.showMessageDialog(null, e, "Błąd", JOptionPane.ERROR_MESSAGE);
}
}
}
}
package testowanie;
public class Testowanie2_Print {
// Jeśli programista, który napisał jakąś funkcję, metodę, klasę ...
// chce tylko sprawdzić czy ta rzecz działa dobrze, może napisać taki prosty programik, który wypisuje coś na ekran.
// I to jest najczęstsze podejście, które jest uważane za złe podejście, przejaw lenistwa itd...
// Napisać i uruchomić jest łatwo, ale:
// - to człowiek sprawdza czy program jest poprawny; NIE MA AUTOMATYCZNEJ ODPOWIEDZI czy jest dobrze czy źle
// - uruchamianie jest jednak bardziej kosztowne niż w testach jednostkowych, bo trzeba spojrzeć na ekran i ocenić czy wyniki / zachowanie są poprawne
public static void main(String[] args) {
var wynik = Funkcje.silnia(5);
System.out.println("silnia(5) = " + wynik);
System.out.println("silnia dla arg 0, 1, 10 : ");
System.out.println(Funkcje.silnia(0));
System.out.println(Funkcje.silnia(1));
System.out.println(Funkcje.silnia(10));
System.out.println();
System.out.println("F(10) = " + Funkcje.fib(10));
}
}
package testowanie;
public class Testowanie3_If {
// Jeśli chcemy, aby program testujący SAM informował, czy testy skończyły się sukcesem, czy porażką,
// można napisać program, który sam w ifach sprawdza wyniki.
// Ten program koncepcyjnie jest już zgodny z ideą testów jednostkowych, chociaż nie używa żadnej biblioteki do testów.
// Patrząc na ten kod aż prosi się o zbudowane pewnego schematu, żeby testy pisać krócej
// i nie robić kopiuj/wklej. Takie schematy istnieją. Najbardziej popularny jest JUnit, inny znany: TestNG.
public static void main(String[] args) {
int ileBledow = 0;
long wynik;
wynik = Funkcje.silnia(0);
if(wynik == 1) {
System.out.println("silnia(0): OK");
} else {
System.out.println("silnia(0): FAIL, zły wynik: " + wynik);
ileBledow++;
}
wynik = Funkcje.silnia(5);
if(wynik == 120) {
System.out.println("silnia(5): OK");
} else {
System.out.println("silnia(5): FAIL, zły wynik: " + wynik);
ileBledow++;
}
wynik = Funkcje.fib(0);
if(wynik == 0) {
System.out.println("fib(0): OK");
} else {
System.out.println("fib(0): FAIL, zły wynik: " + wynik);
ileBledow++;
}
wynik = Funkcje.fib(10);
if(wynik == 55) {
System.out.println("fib(10): OK");
} else {
System.out.println("fib(10): FAIL, zły wynik: " + wynik);
ileBledow++;
}
System.out.println();
if(ileBledow == 0) {
System.out.println("Wszystko OK, nie było błędów");
} else {
System.out.println("Test nie przeszły, było " + ileBledow + " błedów!");
}
}
}
Ciąg dalszy - testy w oparciu i JUnit - jest w katalogu src/test/java
package waluty;
public interface Pobieranie {
/** Pobiera tabelę z bieżącymi kursami walut.
* Zwraca null w przypadku błędów.
*/
Tabela pobierzBiezaceKursy();
/** Pobiera tabelę z archiwalnymi kursami walut z określonej daty.
* Zwraca null w przypadku błędów, np. wtedy, gdy dla danej daty nie istnieje tabela.
*/
Tabela pobierzArchiwalneKursy(String data);
// Dzięki temu, że od Javy 8 w interfejsach można definiować metody statyczne,
// sam interfejs może słuzyć jako "fabryka obiektów".
static Pobieranie utworz(String rodzaj) {
return switch(rodzaj) {
case "xml" -> new PobieranieXML();
case "json" -> new PobieranieJSON();
default -> throw new IllegalArgumentException("Nieobsługiwany rodzaj pobierania: " + rodzaj);
};
}
}
\ No newline at end of file
package waluty;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.LocalDate;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.json.JsonValue;
public class PobieranieJSON implements Pobieranie {
private static final String ADRES = "http://api.nbp.pl/api/exchangerates/tables";
public Tabela pobierzBiezaceKursy() {
return pobierzJsonZAdresu(ADRES + "/a/?format=json");
}
public Tabela pobierzArchiwalneKursy(String data) {
return pobierzJsonZAdresu(ADRES + "/a/" + data + "?format=json");
}
private Tabela pobierzJsonZAdresu(String adres) {
try {
URL url = new URL(adres);
try(InputStream input = url.openStream()) {
return wczytajStream(input);
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private static Tabela wczytajStream(InputStream input) {
try(JsonReader reader = Json.createReader(input)) {
// dokument zawiera [tablicę], a ta tablica zawiera {obiekt}
JsonArray array = reader.readArray();
JsonObject tabela = array.getJsonObject(0);
String typ = tabela.getString("table");
String numer = tabela.getString("no");
LocalDate data = LocalDate.parse(tabela.getString("effectiveDate"));
JsonArray waluty = tabela.getJsonArray("rates");
Tabela wynikowaTabela = new Tabela(typ, numer, data);
for(JsonValue jsonValue : waluty) {
JsonObject jsonObject = jsonValue.asJsonObject();
Waluta waluta = new Waluta(jsonObject.getString("code"),
jsonObject.getString("currency"),
jsonObject.getJsonNumber("mid").bigDecimalValue());
wynikowaTabela.dodajWalute(waluta);
}
return wynikowaTabela;
}
}
}
package waluty;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.time.LocalDate;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class PobieranieXML implements Pobieranie {
private static final String ADRES = "http://api.nbp.pl/api/exchangerates/tables";
/** Pobiera tabelę z bieżącymi kursami walut.
* Zwraca null w przypadku błędów.
*/
public Tabela pobierzBiezaceKursy() {
Document doc = wczytajXmlZAdresu(ADRES + "/a?format=xml");
return tabelaZXml(doc);
}
public Tabela pobierzArchiwalneKursy(String data) {
Document doc = wczytajXmlZAdresu(ADRES + "/A/" + data + "?format=xml");
return tabelaZXml(doc);
}
private Document wczytajXmlZAdresu(String adres) {
// Document Object Model
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
URL url = new URL(adres);
try (InputStream in = url.openStream()) {
return db.parse(in);
}
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
return null;
}
}
private Tabela tabelaZXml(Document doc) {
if (doc == null)
return null;
try {
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
// String numer = xpath.evaluate(
// "/ArrayOfExchangeRatesTable/ExchangeRatesTable/No", doc);
String nazwaTabeli = xpath.evaluate("//Table", doc);
String numerTabeli = xpath.evaluate("//No", doc);
LocalDate data = LocalDate.parse(xpath.evaluate("//EffectiveDate", doc));
Tabela tabela = new Tabela(nazwaTabeli, numerTabeli, data);
NodeList rates = (NodeList) xpath.evaluate("//Rate", doc, XPathConstants.NODESET);
final int n = rates.getLength();
for (int i = 0; i < n; i++) {
Node rate = rates.item(i);
String kod = xpath.evaluate("Code", rate);
String nazwa = xpath.evaluate("Currency", rate);
BigDecimal kurs = new BigDecimal(xpath.evaluate("Mid", rate));
Waluta waluta = new Waluta(kod, nazwa, kurs);
tabela.dodajWalute(waluta);
}
return tabela;
} catch (XPathExpressionException e) {
e.printStackTrace();
return null;
}
}
}
package waluty;
import java.math.BigDecimal;
import java.util.Scanner;
public class PrzelicznikWalutKonsola_v1 {
public static void main(String[] args) {
PobieranieXML pobieranie = new PobieranieXML();
System.out.println("Pobieram aktualne kursy...");
Tabela tabela = pobieranie.pobierzBiezaceKursy();
System.out.println(tabela);
Scanner sc = new Scanner(System.in);
while(true) {
System.out.print("\nPodaj kod waluty: ");
String kod = sc.nextLine().trim().toUpperCase();
if(kod == null || kod.isEmpty())
break;
Waluta waluta = tabela.wyszukaj(kod);
if(waluta == null) {
System.out.println("Nie ma takiej waluty.");
continue;
}
System.out.println("Wybrana waluta: " + waluta.getNazwa());
System.out.println("Aktualny kurs " + waluta.getKurs());
System.out.println();
System.out.print("Podaj kwotę: ");
if(!sc.hasNextBigDecimal()) {
System.out.println("Nieprawidłowa kwota.");
continue;
}
BigDecimal kwota = sc.nextBigDecimal();
sc.nextLine();
System.out.println(kwota + " " + kod + " = "
+ waluta.przeliczNaZlote(kwota) + " PLN");
System.out.println(kwota + " PLN " + " = "
+ waluta.przeliczNaWalute(kwota) + " " + kod);
}
}
}
package waluty;
import java.math.BigDecimal;
import java.util.Scanner;
import org.apache.commons.lang3.StringUtils;
public class PrzelicznikWalutKonsola_v2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Wybierz format komunikacji: xml | json");
String rodzaj = sc.nextLine();
Pobieranie pobieranie = Pobieranie.utworz(rodzaj);
System.out.println("Podaj datę w formacie YYYY-MM-DD lub naciśnij enter, aby pobrać najświeższe kursy.");
String data = sc.nextLine();
Tabela tabela;
if(StringUtils.isBlank(data)) {
System.out.println("Pobieram aktualne kursy...");
tabela = pobieranie.pobierzBiezaceKursy();
} else {
System.out.println("Pobieram kursy archiwalne...");
tabela = pobieranie.pobierzArchiwalneKursy(data);
}
System.out.println(tabela);
while(true) {
System.out.print("\nPodaj kod waluty: ");
String kod = sc.nextLine().trim().toUpperCase();
if(kod == null || kod.isEmpty())
break;
Waluta waluta = tabela.wyszukaj(kod);
if(waluta == null) {
System.out.println("Nie ma takiej waluty.");
continue;
}
System.out.println("Wybrana waluta: " + waluta.getNazwa());
System.out.println("Aktualny kurs " + waluta.getKurs());
System.out.println();
System.out.print("Podaj kwotę: ");
if(!sc.hasNextBigDecimal()) {
System.out.println("Nieprawidłowa kwota.");
continue;
}
BigDecimal kwota = sc.nextBigDecimal();
sc.nextLine();
System.out.println(kwota + " " + kod + " = "
+ waluta.przeliczNaZlote(kwota) + " PLN");
System.out.println(kwota + " PLN " + " = "
+ waluta.przeliczNaWalute(kwota) + " " + kod);
}
}
}
package waluty;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class Tabela {
private final String typ;
private final String numer;
private final LocalDate data;
private final List<Waluta> waluty = new ArrayList<>();
public Tabela(String typ, String numer, LocalDate data) {
this.typ = typ;
this.numer = numer;
this.data = data;
}
public String getTyp() {
return typ;
}
public String getNumer() {
return numer;
}
public LocalDate getData() {
return data;
}
public Collection<Waluta> getWaluty() {
return Collections.unmodifiableList(waluty);
}
@Override
public String toString() {
return "Tabela nr " + numer + " z dnia " + data + ", " + waluty.size() + " walut";
}
public void dodajWalute(Waluta waluta) {
this.waluty.add(waluta);
}
public Waluta wyszukaj(String kod) {
for(Waluta waluta : waluty) {
if(waluta.getKod().equals(kod)) {
return waluta;
}
}
return null;
}
public String[] getKodyWalut() {
return this.waluty.stream()
.map(Waluta::getKod)
.sorted()
.toArray(String[]::new);
}
}
package waluty;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Waluta {
private final String kod;
private final String nazwa;
private final BigDecimal kurs;
public Waluta(String kod, String nazwa, BigDecimal kurs) {
this.kod = kod;
this.nazwa = nazwa;
this.kurs = kurs;
}
public Waluta(String kod, String nazwa, String kurs) {
this(kod, nazwa, new BigDecimal(kurs));
}
public String getKod() {
return kod;
}
public String getNazwa() {
return nazwa;
}
public BigDecimal getKurs() {
return kurs;
}
@Override
public String toString() {
return "Waluta [kod=" + kod + ", nazwa=" + nazwa + ", kurs=" + kurs + "]";
}
public BigDecimal przeliczNaZlote(BigDecimal kwota) {
return kwota.multiply(kurs).setScale(2, RoundingMode.HALF_EVEN);
}
public BigDecimal przeliczNaWalute(BigDecimal kwota) {
return kwota.divide(kurs, 2, RoundingMode.HALF_EVEN);
}
}
package waluty;
import java.time.LocalDate;
import javax.swing.JOptionPane;
public class WyswietlArchiwalneKursyXML {
public static void main(String[] args) {
PobieranieXML pobieranie = new PobieranieXML();
String data = JOptionPane.showInputDialog(null, "Podaj datę w formacie YYYY-MM-DD", LocalDate.now().toString());
Tabela tabela = pobieranie.pobierzArchiwalneKursy(data);
if(tabela == null) {
System.out.println("Nie udało się pobrać. Niewłaściwa data albo problem z siecią.");
} else {
System.out.println("Pobrana tabela: " + tabela);
for (Waluta waluta : tabela.getWaluty()) {
System.out.println( " * " + waluta);
}
}
}
}
package waluty;
public class WyswietlBiezaceKursyJSON {
public static void main(String[] args) {
PobieranieJSON pobieranie = new PobieranieJSON();
Tabela tabela = pobieranie.pobierzBiezaceKursy();
System.out.println("Pobrana tabela: " + tabela);
for (Waluta waluta : tabela.getWaluty()) {
System.out.println( " * " + waluta);
}
}
}
package waluty;
public class WyswietlWszystkieKursy_XML {
public static void main(String[] args) {
System.out.println("Startujemy...");
PobieranieXML pobieranie = new PobieranieXML();
Tabela tabela = pobieranie.pobierzBiezaceKursy();
System.out.println("Mam tabelę: " + tabela);
for(Waluta waluta : tabela.getWaluty()) {
System.out.println(waluta);
}
}
}
package testowanie;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class FunkcjeTest {
@Test
void testSilnia0() {
// normalnie w testach nie robi się żadnych printów
// tu tylko eksperyment, żeby zobaczyć, że ten kod naprawdę się wykonuje
System.out.println("a kuku");
// W typowym teście wykonujemy scenariusz testowy - tutaj tylko wywołanie funkcji, ale bywają bardziej rozbudowane przykłady
long wynik = Funkcje.silnia(0);
// A pod koniec testu za pomocą asercji sprawdzamy czy kod zadziałał prawidłowo.
// Tutaj sprawdzamy wynik, ale często sprawdza się też stan obiektów itp. rzeczy
// W języku Java istnieje prosta konstrukcja assert:
// assert wynik == 1;
// ale rzadko się jej używa, bo daje marne komunikaty w razie błędu; tylko numer linii
// można podać własny komunikat, ale jest to dużo roboty:
// assert wynik == 1 : "dla silnia 0 wyszedł zły wynik " + wynik;
// Najczęście używa się assertEquals pochodzącej z biblioteki JUnit,
assertEquals(1, wynik);
// albo dedykowanych bibliotek do pisania asercji, np. AssertJ
// Technicznie to działa tak:
// - gdy asercja stwierdza, że warunek jest spełniony (jest OK), to nic się dzieje i test idzie dalej
// - gdy asercja stwierdza, że warunek nie jest spełniony, coś się nie zgadza,
// to wyrzucany jest wyjątek AssertionError i dalsza część testu się nie wykona
// Gdy cały test dojdzie do końca bez wyjątku, to jest sukces.
// Gdy test kończy się wyjątkiem AssertionError - to "failuje"
// Gdy test kończy się innym wyjątkiem (np. NPE) - jest to liczone jako "error"
}
@Test
void testSilnia() {
long wynik = Funkcje.silnia(5);
assertEquals(120, wynik);
}
@Test
void testFib() {
long wynik = Funkcje.fib(10);
assertEquals(55, wynik);
}
}
package testowanie;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class KontoTest {
@Test
void testConstructor() {
Konto konto = new Konto(1234, 1000, "Ala Kowalska");
assertEquals(1234, konto.getNumer());
assertEquals(1000, konto.getSaldo());
assertEquals("Ala Kowalska", konto.getWlasciciel());
}
// Test-Driven Development (TDD)
// to taki sposób pracy (metodologia pracy), że
// najpierw piszemy test, który nie działa ("fejluje")
// a dopiero potem uzupełniamy implementację do momentu, aż test "zaskoczy"
@Test
void testToString() {
Konto konto = new Konto(1234, 1000, "Ala Kowalska");
assertEquals("Konto nr 1234, saldo 1000, wł. Ala Kowalska", konto.toString());
}
@Test
void testWplata() {
Konto konto = new Konto(1234, 1000, "Ala Kowalska");
konto.wplata(300);
assertEquals(1300, konto.getSaldo());
}
@Test
void testWyplata() throws BrakSrodkow {
Konto konto = new Konto(1234, 1000, "Ala Kowalska");
konto.wyplata(400);
assertEquals(600, konto.getSaldo());
}
}
package testowanie;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class KontoTest_WersjaRozszerzona {
private Konto konto;
@BeforeEach
void setUp() {
// ta metoda zostanie uruchomiona bezpośrednio przed każdym testem
// dokonuje się w niej inicjalizacji danych potrzebnych do testu - "stan początkowy" / "fixture"
konto = new Konto(1234, 1000, "Ala Kowalska");
}
@Test
void testConstructor() {
assertEquals(1234, konto.getNumer());
assertEquals(1000, konto.getSaldo());
assertEquals("Ala Kowalska", konto.getWlasciciel());
}
@Test
void testToString() {
assertEquals("Konto nr 1234, saldo 1000, wł. Ala Kowalska", konto.toString());
}
@Test
void testWplata() {
konto.wplata(300);
assertEquals(1300, konto.getSaldo());
}
@Test
void testWyplata() throws BrakSrodkow {
konto.wyplata(400);
assertEquals(600, konto.getSaldo());
}
// Temat sprawdzania wyjątków w testach.
// Aby stwierdzić, że w pewnej sytuacji metoda POWINNA skończyć się wyjątkiem,
// możemy użyć odpowiednio try-catch (testWplataTry)
// albo użyć dedykowanych rozwiązań JUnit - dalsze przykłady
@Test
void testWplataTry() {
try {
konto.wplata(-100);
// Gdyby nie było wyjątku i program doszedłby do tego miejsca,
// test powinien skończyć się porażką
fail("Powinien być wyjątek, a nie ma.");
} catch(IllegalArgumentException e) {
// Jeśli wyłapiemy wyjątek, możemy sprawdzić w teście jego szczegóły,
// np. jaki jest message (bardzo rzadko takie rzeczy sprawdza się w testach, ale można):
assertEquals("Niedodatnia kwota w metodzie wplata", e.getMessage());
} finally {
// Możemy też upewnić się, że w przypadku wystąpienia wyjątku stan konta się nie zmienił.
assertEquals(1000, konto.getSaldo());
}
}
// Analogiczny test można też napisać wykorzystując nową funkcjonalność JUnit 5:
@Test
void testWplataUjemna_v1() {
// assertThrows wykonuje podany fragment kodu (w praktyce podaje się wyrażenie lambda)
// i sprawdza czy ten kończy się podanym rodzajem wyjątku.
// Jeśli jest wyjątek - dobrze
// Jeśli nie ma wyjątku - test failuje
assertThrows(IllegalArgumentException.class, () -> {
konto.wplata(-100);
});
// Dodatkowo po wykonaniu assertThrows możemy sprawdzić jaki jest stan końcowy,
// np. czy saldo się nie zmieniło.
assertEquals(1000, konto.getSaldo());
}
// Aby sprawdzić jaki jest message w wyjątku itp, możemy odebrać obiekt wyjątku i sprawdzić bezpośrednio
@Test
void testWplataUjemna_v2() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
konto.wplata(-100);
});
assertEquals("Niedodatnia kwota w metodzie wplata", exception.getMessage());
assertEquals(1000, konto.getSaldo());
}
@Test
void testWyplataUjemna() {
assertThrows(IllegalArgumentException.class, () -> {
konto.wyplata(-100);
});
assertEquals(1000, konto.getSaldo());
}
@Test
void testBrakSrodkow() {
assertThrows(BrakSrodkow.class, () -> {
konto.wyplata(1300);
});
assertEquals(1000, konto.getSaldo());
}
}
package waluty;
import static org.junit.jupiter.api.Assertions.*;
import java.math.BigDecimal;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class WalutaTest {
private Waluta dolar;
@BeforeEach
void setUp() throws Exception {
dolar = new Waluta("USD", "dolar", "4.0000");
}
@Test
void testPrzeliczNaZlote() {
BigDecimal kwota = BigDecimal.valueOf(1000);
BigDecimal oczekiwanyWynik = new BigDecimal("4000.00");
BigDecimal wynik = dolar.przeliczNaZlote(kwota);
assertEquals(oczekiwanyWynik, wynik);
}
@Test
void testPrzeliczNaZlote_Zaokraglanie() {
Waluta innaWaluta = new Waluta("ABC", "przykładowa waluta", new BigDecimal("2.2222"));
BigDecimal kwota = new BigDecimal("3.00");
BigDecimal oczekiwanyWynik = new BigDecimal("6.67");
BigDecimal wynik = innaWaluta.przeliczNaZlote(kwota);
assertEquals(oczekiwanyWynik, wynik);
}
@Test
void testPrzeliczNaWalute() {
BigDecimal kwota = new BigDecimal("400.00");
BigDecimal oczekiwanyWynik = new BigDecimal("100.00");
BigDecimal wynik = dolar.przeliczNaWalute(kwota);
assertEquals(oczekiwanyWynik, wynik);
}
@Test
void testPrzeliczNaWalute_Zaokraglanie() {
Waluta innaWaluta = new Waluta("ABC", "przykładowa waluta", new BigDecimal("3.0000"));
BigDecimal kwota = new BigDecimal("200.00");
BigDecimal oczekiwanyWynik = new BigDecimal("66.67");
BigDecimal wynik = innaWaluta.przeliczNaWalute(kwota);
assertEquals(oczekiwanyWynik, wynik);
}
}
/target/
/.settings/
/.classpath
/.project
/*.iml
/.idea/
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.alx.kjava</groupId>
<artifactId>PC35-RestTechnicznie</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>${project.artifactId}</name>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/a")
public class A {
@GET
public String get() {
return "AAAAAAAA";
}
}
package rest;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
// przykładowy adres
// http://localhost:8080/PC35-RestTechniczne/rest1/a
@ApplicationPath("/rest1")
public class AplikacjaRestowa1 extends Application {
// Definicja aplikacji JAX-RS, w której skład wchodzą wszystkie dostępne w projekcie klasy oznaczone adnotacjami @Path lub @Provider
// Klasy zasobów działają w trybie "per-request" - dla każdego zapytania jest tworzony nowy obiekt klasy zasobu
// np. jest tworzony nowy wyzerowany licznik - /rest1/licznik będzie zawsze zwracać 1
}
package rest;
import java.util.HashSet;
import java.util.Set;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/rest2")
public class AplikacjaRestowa2 extends Application {
// Sam podaję klasy, które wchodzą w skład aplikacji JAX-RS
// Podaj się zarówno zasoby (@Path), jak i rozszerzenia (@Provider)
// Klasy zasobów działają w trybie "per-request"
// Czyli licznik /rest2/licznik będzie zawsze zwracać 1
@Override
public Set<Class<?>> getClasses() {
// Są klasy A i B, a nie ma C
HashSet<Class<?>> zbior = new HashSet<>();
zbior.add(A.class);
zbior.add(B.class);
zbior.add(Licznik.class);
zbior.add(Kontekst.class);
zbior.add(EBean.class);
return zbior;
// Od Javy 9:
// return Set.of(A.class, B.class, Licznik.class, Kontekst.class, EBean.class);
}
}
package rest;
import java.util.HashSet;
import java.util.Set;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/rest3")
public class AplikacjaRestowa3 extends Application {
// Sam podaję klasy, które wchodzą w skład aplikacji JAX-RS
// Podaj się zarówno zasoby (@Path), jak i rozszerzenia (@Provider)
@Override
public Set<Class<?>> getClasses() {
System.out.println("Aplikacja3.getClasses");
// te klasy działają w trybie "per-request"
HashSet<Class<?>> zbior = new HashSet<>();
zbior.add(A.class);
zbior.add(Kontekst.class);
return zbior;
}
@Override
public Set<Object> getSingletons() {
System.out.println("Aplikacja3.getSingletons");
Licznik licznik = new Licznik();
licznik.setLicznik(100);
// Te klasy działają w trybie singleton - ten sam obiekt obsługuje różne zapytania.
// Czyli licznik /rest3/licznik będzie zwraca coraz większe wartości
HashSet<Object> zbior = new HashSet<>();
zbior.add(new B());
zbior.add(new Kontekst());
zbior.add(licznik);
zbior.add(new EBean());
return zbior;
}
}
package rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/b")
public class B {
{ System.out.println("Powstaje obiekt B"); }
@GET
public String get() {
return "BBBB";
}
}
package rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/c")
public class C {
@GET
public String get() {
return "CCC";
}
}
package rest;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.ejb.Stateless;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/ejb")
@Stateless
// @Singleton // też działa
public class EBean implements Serializable {
private static final long serialVersionUID = -2627946805995054930L;
private AtomicInteger licznik = new AtomicInteger();
// Dzięki temu, że jesteśmy w klasie EJB, można korzystać z adnotacji Java EE
// @PersistenceUnit
// private EntityManager em;
public EBean() {
System.out.println("EBean konstruktor");
}
@PostConstruct
public void postConstruct() {
System.out.println("EBean @PostConstruct");
}
@PreDestroy
public void preDestroy() {
System.out.println("EBean @PreDestroy");
}
@GET
public String get() {
return "Hello EJB. Licznik = " + licznik.incrementAndGet();
}
}
package rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
@Path("/calc")
public class Kalkulator {
@GET
@Path("/{x}+{y}")
public long dodaj(@PathParam("x") long a, @PathParam("y") long b) {
return a+b;
}
@GET
@Path("/{x}-{y}")
public long odejmij(@PathParam("x") long a, @PathParam("y") long b) {
return a-b;
}
@GET
@Path("/{x}*{y}")
public long pomnoz(@PathParam("x") long a, @PathParam("y") long b) {
return a*b;
}
@GET
@Path("/{x}/{y}")
public long podziel(@PathParam("x") long a, @PathParam("y") long b) {
return a/b;
}
}
package rest;
import java.util.Arrays;
import java.util.Map;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Request;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.UriInfo;
@Path("/kontekst")
@Produces("text/plain")
public class Kontekst {
@GET
public String info(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Context Request restRequest,
@Context HttpHeaders headers,
@Context ServletContext servletContext,
@Context ServletConfig servletConfig,
@Context HttpServletRequest servletRequest,
@Context HttpServletResponse servletResponse
) {
StringBuilder b = new StringBuilder();
b.append("UriInfo\n");
b.append("Base URI : ").append(uriInfo.getBaseUri()).append('\n');
b.append("Absolute path: ").append(uriInfo.getAbsolutePath()).append('\n');
b.append("Path : ").append(uriInfo.getPath()).append('\n');
b.append("\nHttpHeaders\n");
b.append(headers.getRequestHeaders());
// Request restRequest - wiąże się z obsługą keszowania i typów zawartości
b.append("\nServletContext\n");
b.append("ServlerInfo: ").append(servletContext.getServerInfo()).append('\n');
b.append("\nServletRequest\n");
b.append("RemoteAddr: ").append(servletRequest.getRemoteAddr()).append('\n');
b.append("LocalAddr : ").append(servletRequest.getLocalAddr()).append('\n');
b.append("URI : ").append(servletRequest.getRequestURI()).append('\n');
b.append("Parametry :\n");
for (Map.Entry e : servletRequest.getParameterMap().entrySet()) {
String[] v = (String[]) e.getValue();
b.append(" * " + e.getKey() + " : " + Arrays.toString(v) + "\n");
}
b.append("\nZalogowany wg serwletu: ").append(servletRequest.getRemoteUser()).append('\n');
b.append("Zalogowany wg SecurityContext: ").append(securityContext.getUserPrincipal()).append('\n');
return b.toString();
}
}
package rest;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@Path("/licznik")
@Produces("text/plain")
@Consumes("text/plain")
public class Licznik {
private int licznik = 0;
@GET
public synchronized int getLicznik() {
return ++licznik;
}
@PUT
public synchronized void setLicznik(int licznik) {
this.licznik = licznik;
}
}
package rest;
import java.time.LocalDateTime;
import java.util.Arrays;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.MatrixParam;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;
/* Przykładowe adresy z parametrami:
/rest1/parametry/query?a=Ala&b=Ola&b=Ela&t=Basia&t=Kasia&t=Zosia
/rest1/parametry/matrix;a=Ala;b=Ola;b=Ela;t=Basia;t=Kasia;t=Zosia
*/
@Path("/parametry")
@Produces("text/plain")
public class Parametry {
@GET
@Path("/query")
public String query(
@QueryParam("a") String a,
@QueryParam("b") String b,
@QueryParam("t") String[] t) {
return "Parametr a = " + a
+ "\nParametr b = " + b
+ "\nTablica: " + Arrays.toString(t);
}
@GET
@Path("/matrix")
public String matrix(
@MatrixParam("a") String a,
@MatrixParam("b") String b,
@MatrixParam("t") String[] t) {
return "Parametr a = " + a
+ "\nParametr b = " + b
+ "\nTablica: " + Arrays.toString(t);
}
// /rest1/parametry/path/Ala/123/98765qwerty@res-zta
@GET
@Path("/path/{a}/{b}/{cyfry:\\d+}{litery:\\w+}{reszta}")
public String pathParam(
@PathParam("a") String a,
@PathParam("b") String b,
@PathParam("cyfry") String cyfry,
@PathParam("litery") String litery,
@PathParam("reszta") String reszta
) {
return "Parametr a = " + a
+ "\nParametr b = " + b
+ "\nCyfry: " + cyfry
+ "\nLitery: " + litery
+ "\nReszta: " + reszta;
}
@GET
@Path("/headers")
public String headers(
@HeaderParam("accept") String accept,
@HeaderParam("user-agent") String agent
) {
return "Accept: " + accept
+ "\nUser-Agent: " + agent;
}
@GET
@Path("/cookies")
public String cookies(
@CookieParam("ciacho") String ciacho,
@CookieParam("JSESSIONID") String sessionId
) {
return "Ciacho: " + ciacho
+ "\nSesja: " + sessionId;
}
@GET
@Path("/ustaw")
// ustawia ciacho
public Response ustawCiacho() {
String ciacho = LocalDateTime.now().toString();
return Response.ok()
.cookie(new NewCookie("ciacho", ciacho))
.type("text/plain")
.entity("Ustawiam ciacho na: " + ciacho)
.build();
}
}
package rest;
import java.util.concurrent.atomic.AtomicInteger;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
@Path("/sesja")
public class Sesja {
@Context
private HttpServletRequest request;
@Produces("text/plain")
@GET
public int licznik() {
HttpSession sesja = request.getSession();
AtomicInteger licznik = null;
synchronized (sesja) {
licznik = (AtomicInteger) sesja.getAttribute("x");
if(licznik == null) {
licznik = new AtomicInteger(100);
sesja.setAttribute("x", licznik);
sesja.setMaxInactiveInterval(30); // po 30s sesja wygasa
}
}
return licznik.getAndIncrement();
// tak jakby return licznik++
}
}
package serwlet;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/serwlet")
public class ZwyklySerwlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Jestem zwyklym serwletem");
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Elementy techniczne JAX-RS</title>
</head>
<body>
<h1>Elementy techniczne JAX-RS</h1>
<ul>
<li><a href="serwlet">zwykły serwlet</a> - działający poza JAX-RS</li>
</ul>
<h2>Trzy wersje aplikacji</h2>
<ol>
<li>rest1 - ustawienia domyślne - <i>per request</i>
<ul>
<li><a href="rest1/a">A</a> - istnieje</li>
<li><a href="rest1/b">B</a> - istnieje</li>
<li><a href="rest1/c">C</a> - istnieje</li>
<li><a href="rest1/licznik">Licznik</a> - zawsze 1</li>
</ul>
</li>
<li>rest2 - <code>getClasses()</code> - <i>per request</i>
<ul>
<li><a href="rest2/a">A</a> - istnieje</li>
<li><a href="rest2/b">B</a> - istnieje</li>
<li><a href="rest2/c">C</a> - nie istnieje</li>
<li><a href="rest2/licznik">Licznik</a> - zawsze 1</li>
</ul>
</li>
<li>rest3 - <code>getSingletons()</code> - niektóre zasoby jako singletony
<ul>
<li><a href="rest3/a">A</a> - istnieje</li>
<li><a href="rest3/b">B</a> - istnieje</li>
<li><a href="rest3/c">C</a> - nie istnieje</li>
<li><a href="rest3/licznik">Licznik</a> - globalny</li>
</ul>
</li>
</ol>
<h2>Elementy techniczne</h2>
<ul>
<li><a href="rest1/kontekst">kontekst</a> - informacje wstrzyknięte za pomocą @Context</li>
<li><a href="rest1/sesja">sesja</a> - licznik w sesji</li>
<li><b>Parametry</b>
<ul>
<li><a href="rest1/parametry/query?a=Ala&b=Ola&b=Ela&t=Basia&t=Kasia&t=Zosia">@QueryParam</a></li>
<li><a href="rest1/parametry/matrix;a=Ala;b=Ola;b=Ela;t=Basia;t=Kasia;t=Zosia">@MatrixParam</a></li>
<li><a href="rest1/parametry/path/Ala/123/98765qwerty@res-zta">@PathParam</a></li>
<li><a href="rest1/parametry/headers">@HeaderParam</a></li>
<li><a href="rest1/parametry/cookies">@CookieParam</a></li>
<li><a href="rest1/parametry/ustaw">Ustaw ciacho</a></li>
</ul>
</li>
</ul>
<h2>Kalkulator</h2>
<ul>
<li><a href="rest1/calc/12+13">dodawanie</a></li>
<li><a href="rest1/calc/12*13">mnożenie</a></li>
</ul>
</body>
</html>
\ No newline at end of file
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>pl.alx.kjava</groupId>
<artifactId>PC37-Jersey</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>PC37-Jersey</name>
<description>Usługa JAX-RS w Springu</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.demo;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.context.annotation.Configuration;
import com.demo.proste.Hello;
import com.demo.proste.Kalkulator;
import com.demo.proste.Kontekst;
import com.demo.proste.Parametry;
import com.demo.proste.Time;
import com.demo.sklep.rest.ROrder;
import com.demo.sklep.rest.RProduct;
@Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(Hello.class);
register(Kontekst.class);
register(Parametry.class);
register(Kalkulator.class);
register(Time.class);
register(RProduct.class);
register(ROrder.class);
}
}
package com.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Pc37JerseyApplication {
public static void main(String[] args) {
SpringApplication.run(Pc37JerseyApplication.class, args);
}
}
package com.demo.proste;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@Path("/hello")
public class Hello {
@GET
@Produces("text/plain")
public String hello() {
return "Hello Jersey";
}
}
package com.demo.proste;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
@Path("/calc")
public class Kalkulator {
@GET
@Path("/{x}+{y}")
public long dodaj(@PathParam("x") long a, @PathParam("y") long b) {
return a+b;
}
@GET
@Path("/{x}-{y}")
public long odejmij(@PathParam("x") long a, @PathParam("y") long b) {
return a-b;
}
@GET
@Path("/{x}*{y}")
public long pomnoz(@PathParam("x") long a, @PathParam("y") long b) {
return a*b;
}
@GET
@Path("/{x}/{y}")
public long podziel(@PathParam("x") long a, @PathParam("y") long b) {
return a/b;
}
}
package com.demo.proste;
import java.util.Arrays;
import java.util.Map;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Request;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.UriInfo;
@Path("/kontekst")
@Produces("text/plain")
public class Kontekst {
@GET
public String info(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Context Request restRequest,
@Context HttpHeaders headers,
@Context ServletContext servletContext,
@Context ServletConfig servletConfig,
@Context HttpServletRequest servletRequest,
@Context HttpServletResponse servletResponse
) {
StringBuilder b = new StringBuilder();
b.append("UriInfo\n");
b.append("Base URI : ").append(uriInfo.getBaseUri()).append('\n');
b.append("Absolute path: ").append(uriInfo.getAbsolutePath()).append('\n');
b.append("Path : ").append(uriInfo.getPath()).append('\n');
b.append("\nHttpHeaders\n");
b.append(headers.getRequestHeaders());
// Request restRequest - wiąże się z obsługą keszowania i typów zawartości
b.append("\nServletContext\n");
b.append("ServlerInfo: ").append(servletContext.getServerInfo()).append('\n');
b.append("\nServletRequest\n");
b.append("RemoteAddr: ").append(servletRequest.getRemoteAddr()).append('\n');
b.append("LocalAddr : ").append(servletRequest.getLocalAddr()).append('\n');
b.append("URI : ").append(servletRequest.getRequestURI()).append('\n');
b.append("Parametry :\n");
for (Map.Entry e : servletRequest.getParameterMap().entrySet()) {
String[] v = (String[]) e.getValue();
b.append(" * " + e.getKey() + " : " + Arrays.toString(v) + "\n");
}
b.append("\nZalogowany wg serwletu: ").append(servletRequest.getRemoteUser()).append('\n');
b.append("Zalogowany wg SecurityContext: ").append(securityContext.getUserPrincipal()).append('\n');
return b.toString();
}
}
package com.demo.proste;
import java.time.LocalDateTime;
import java.util.Arrays;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.MatrixParam;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;
/* Przykładowe adresy z parametrami:
/rest1/parametry/query?a=Ala&b=Ola&b=Ela&t=Basia&t=Kasia&t=Zosia
/rest1/parametry/matrix;a=Ala;b=Ola;b=Ela;t=Basia;t=Kasia;t=Zosia
*/
@Path("/parametry")
@Produces("text/plain")
public class Parametry {
@GET
@Path("/query")
public String query(
@QueryParam("a") String a,
@QueryParam("b") String b,
@QueryParam("t") String[] t) {
return "Parametr a = " + a
+ "\nParametr b = " + b
+ "\nTablica: " + Arrays.toString(t);
}
@GET
@Path("/matrix")
public String matrix(
@MatrixParam("a") String a,
@MatrixParam("b") String b,
@MatrixParam("t") String[] t) {
return "Parametr a = " + a
+ "\nParametr b = " + b
+ "\nTablica: " + Arrays.toString(t);
}
// /rest1/parametry/path/Ala/123/98765qwerty@res-zta
@GET
@Path("/path/{a}/{b}/{cyfry:\\d+}{litery:\\w+}{reszta}")
public String pathParam(
@PathParam("a") String a,
@PathParam("b") String b,
@PathParam("cyfry") String cyfry,
@PathParam("litery") String litery,
@PathParam("reszta") String reszta
) {
return "Parametr a = " + a
+ "\nParametr b = " + b
+ "\nCyfry: " + cyfry
+ "\nLitery: " + litery
+ "\nReszta: " + reszta;
}
@GET
@Path("/headers")
public String headers(
@HeaderParam("accept") String accept,
@HeaderParam("user-agent") String agent
) {
return "Accept: " + accept
+ "\nUser-Agent: " + agent;
}
@GET
@Path("/cookies")
public String cookies(
@CookieParam("ciacho") String ciacho,
@CookieParam("JSESSIONID") String sessionId
) {
return "Ciacho: " + ciacho
+ "\nSesja: " + sessionId;
}
@GET
@Path("/ustaw")
// ustawia ciacho
public Response ustawCiacho() {
String ciacho = LocalDateTime.now().toString();
return Response.ok()
.cookie(new NewCookie("ciacho", ciacho))
.type("text/plain")
.entity("Ustawiam ciacho na: " + ciacho)
.build();
}
}
package com.demo.proste;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@Path("/dt")
@Produces("text/plain")
public class Time {
private LocalDateTime dt = LocalDateTime.now();
{
System.out.println("Jest tworzony obiekt klasy DataICzas, dt = " + dt);
}
@GET
public LocalDateTime dt() {
return dt;
}
@Path("/date")
@GET
public LocalDate data() {
return dt.toLocalDate();
}
@Path("/date/year")
@GET
public int year() {
return dt.getYear();
}
@Path("/date/month")
@GET
public int month() {
return dt.getMonthValue();
}
@Path("/date/day")
@GET
public int day() {
return dt.getDayOfMonth();
}
@Path("/date/wd")
@GET
public String weekday() {
return dt.getDayOfWeek().toString();
}
@Path("/date/doy")
@GET
public int doy() {
return dt.getDayOfYear();
}
@Path("/time")
@GET
public LocalTime time() {
return dt.toLocalTime();
}
@Path("/time/hour")
@GET
public int hour() {
return dt.getHour();
}
@Path("/time/minute")
@GET
public int minute() {
return dt.getMinute();
}
@Path("/time/second")
@GET
public int second() {
return dt.getSecond();
}
}
package com.demo.sklep.model;
import java.io.Serializable;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Pattern;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* The persistent class for the customers database table.
*
*/
@Entity
@Table(name="customers")
@NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c")
public class Customer extends WspolnaNadklasa implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="customer_email")
@Email
private String customerEmail;
private String address;
private String city;
@Column(name="customer_name")
private String customerName;
@Column(name="phone_number")
private String phoneNumber;
@Column(name="postal_code")
@Pattern(regexp="\\d{2}-\\d{3}")
private String postalCode;
//bi-directional many-to-one association to Order
@OneToMany(mappedBy="customer")
@JsonIgnore
private List<Order> orders;
public Customer() {
}
public String getCustomerEmail() {
return this.customerEmail;
}
public void setCustomerEmail(String customerEmail) {
this.customerEmail = customerEmail;
}
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return this.city;
}
public void setCity(String city) {
this.city = city;
}
public String getCustomerName() {
return this.customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getPhoneNumber() {
return this.phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPostalCode() {
return this.postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public List<Order> getOrders() {
return this.orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
public Order addOrder(Order order) {
getOrders().add(order);
order.setCustomer(this);
return order;
}
public Order removeOrder(Order order) {
getOrders().remove(order);
order.setCustomer(null);
return order;
}
}
\ No newline at end of file
package com.demo.sklep.model;
import java.io.Serializable;
import jakarta.persistence.*;
import java.util.Date;
import java.sql.Timestamp;
import java.util.List;
/**
* The persistent class for the orders database table.
*
*/
@Entity
@Table(name="orders")
@NamedQuery(name="Order.findAll", query="SELECT o FROM Order o")
public class Order extends WspolnaNadklasa implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="order_id", updatable=false)
private Integer orderId;
@Temporal(TemporalType.DATE)
@Column(name="delivery_date")
private Date deliveryDate;
@Column(name="order_date")
private Timestamp orderDate;
private String status;
//bi-directional many-to-one association to OrderProduct
@OneToMany(mappedBy="order")
private List<OrderProduct> orderProducts;
//bi-directional many-to-one association to Customer
@ManyToOne
@JoinColumn(name="customer_email")
private Customer customer;
public Order() {
}
public Integer getOrderId() {
return this.orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public Date getDeliveryDate() {
return this.deliveryDate;
}
public void setDeliveryDate(Date deliveryDate) {
this.deliveryDate = deliveryDate;
}
public Timestamp getOrderDate() {
return this.orderDate;
}
public void setOrderDate(Timestamp orderDate) {
this.orderDate = orderDate;
}
public String getStatus() {
return this.status;
}
public void setStatus(String status) {
this.status = status;
}
public List<OrderProduct> getOrderProducts() {
return this.orderProducts;
}
public void setOrderProducts(List<OrderProduct> orderProducts) {
this.orderProducts = orderProducts;
}
public OrderProduct addOrderProduct(OrderProduct orderProduct) {
getOrderProducts().add(orderProduct);
orderProduct.setOrder(this);
return orderProduct;
}
public OrderProduct removeOrderProduct(OrderProduct orderProduct) {
getOrderProducts().remove(orderProduct);
orderProduct.setOrder(null);
return orderProduct;
}
public Customer getCustomer() {
return this.customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
\ No newline at end of file
package com.demo.sklep.model;
import java.io.Serializable;
import jakarta.persistence.*;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Min;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* The persistent class for the order_products database table.
*
*/
@Entity
@Table(name="order_products")
@NamedQuery(name="OrderProduct.findAll", query="SELECT o FROM OrderProduct o")
public class OrderProduct extends WspolnaNadklasa implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private OrderProductPK id;
@Column(name="actual_price")
@DecimalMin("0.01")
private BigDecimal actualPrice;
@Column(name="actual_vat")
private BigDecimal actualVat;
@Min(1)
private Integer quantity;
//bi-directional many-to-one association to Order
@ManyToOne
@JoinColumn(name="order_id", insertable=false, updatable=false)
@JsonIgnore
private Order order;
//uni-directional many-to-one association to Product
@ManyToOne
@JoinColumn(name="product_id", insertable=false, updatable=false)
private Product product;
public OrderProduct() {
}
public OrderProductPK getId() {
return this.id;
}
public void setId(OrderProductPK id) {
this.id = id;
}
public BigDecimal getActualPrice() {
return this.actualPrice;
}
public void setActualPrice(BigDecimal actualPrice) {
this.actualPrice = actualPrice;
}
public BigDecimal getActualVat() {
return this.actualVat;
}
public void setActualVat(BigDecimal actualVat) {
this.actualVat = actualVat;
}
public Integer getQuantity() {
return this.quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Order getOrder() {
return this.order;
}
public void setOrder(Order order) {
this.order = order;
}
public Product getProduct() {
return this.product;
}
public void setProduct(Product product) {
this.product = product;
}
}
\ No newline at end of file
package com.demo.sklep.model;
import java.io.Serializable;
import jakarta.persistence.*;
/**
* The primary key class for the order_products database table.
*
*/
@Embeddable
public class OrderProductPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
@Column(name="order_id", insertable=false, updatable=false)
private Integer orderId;
@Column(name="product_id", insertable=false, updatable=false)
private Integer productId;
public OrderProductPK() {
}
public Integer getOrderId() {
return this.orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public Integer getProductId() {
return this.productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof OrderProductPK)) {
return false;
}
OrderProductPK castOther = (OrderProductPK)other;
return
this.orderId.equals(castOther.orderId)
&& this.productId.equals(castOther.productId);
}
public int hashCode() {
final int prime = 31;
int hash = 17;
hash = hash * prime + this.orderId.hashCode();
hash = hash * prime + this.productId.hashCode();
return hash;
}
}
\ No newline at end of file
package com.demo.sklep.model;
import java.io.Serializable;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
/**
* The persistent class for the products database table.
*
*/
@Entity
@Table(name="products")
@NamedQuery(name="Product.findAll", query="SELECT p FROM Product p")
public class Product extends WspolnaNadklasa implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="product_id", updatable=false)
private Integer productId;
private String description;
@NotNull
@DecimalMin("0.01")
private BigDecimal price;
@Column(name="product_name")
@NotNull
@Size(min=2, max=10)
private String productName;
@DecimalMin("0.00")
@DecimalMax("0.50")
private BigDecimal vat;
public Product() {
}
public Integer getProductId() {
return this.productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public BigDecimal getPrice() {
return this.price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public BigDecimal getVat() {
return this.vat;
}
public void setVat(BigDecimal vat) {
this.vat = vat;
}
}
\ No newline at end of file
package com.demo.sklep.model;
import java.lang.reflect.Field;
abstract class WspolnaNadklasa {
@Override
public String toString() {
StringBuilder result = new StringBuilder();
Class<? extends WspolnaNadklasa> klasa = this.getClass();
result.append(klasa.getSimpleName()).append(" [");
int fieldNo = 0;
for(Field field : klasa.getDeclaredFields())
try {
if(fieldNo++ > 0) {
result.append(", ");
}
Object value;
if(field.trySetAccessible()) {
value = field.get(this);
} else {
value = "!";
}
result.append(field.getName()).append('=').append(value);
} catch (IllegalArgumentException | IllegalAccessException e) {
System.err.println(e);
}
result.append("]");
return result.toString();
}
}
package com.demo.sklep.photo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
@Component
public class PhotoUtil {
@Value("${alx.photo_dir}")
private String photoDir;
private static final String EXT = ".jpg";
public File getFile(int productId) {
Path path = getPath(productId);
File file = path.toFile();
if(file.exists()) {
return file;
} else {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cannot read photo for product id = " + productId);
}
}
public byte[] readBytes(int productId) {
Path path = getPath(productId);
try {
return Files.readAllBytes(path);
} catch (IOException e) {
// System.err.println(e);
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Cannot read photo for product id = " + productId);
}
}
public void writeStream(int productId, InputStream inputStream) {
try {
Path path = getPath(productId);
Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
// wypisujemy błąd, ale metoda kończy się normalnie
e.printStackTrace();
}
}
public void writeBytes(int productId, byte[] bytes) {
try {
Path path = getPath(productId);
Files.write(path, bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
private Path getPath(int productId) {
String fileName = productId + EXT;
return Paths.get(photoDir, fileName);
}
}
package com.demo.sklep.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.demo.sklep.model.Customer;
@Repository
public interface CustomerRepository extends JpaRepository<Customer, String> {
}
package com.demo.sklep.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.demo.sklep.model.Order;
@Repository
public interface OrderRepository extends JpaRepository<Order, Integer> {
}
package com.demo.sklep.repository;
import java.math.BigDecimal;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.demo.sklep.model.Product;
@Repository
public interface ProductRepository extends JpaRepository<Product, Integer> {
List<Product> findByProductName(String name);
List<Product> findByProductNameContaining(String name);
List<Product> findByProductNameContainingIgnoringCase(String name);
List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
List<Product> findByProductNameContainingIgnoringCaseAndPriceBetween(String name, BigDecimal min, BigDecimal max);
}
package com.demo.sklep.rest;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
import com.demo.sklep.model.Order;
import com.demo.sklep.repository.OrderRepository;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
@Path("/orders")
@Produces("application/json")
@Consumes("application/json")
public class ROrder {
@Autowired
private OrderRepository orderRepository;
@GET
public List<Order> readAll() {
return orderRepository.findAll();
}
@Path("/{id}")
@GET
public Order readOne(@PathParam("id") int id) {
Optional<Order> order = orderRepository.findById(id);
if(order.isPresent()) {
return order.get();
} else {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Nie ma zamówienia o numerze " + id);
}
}
}
package com.demo.sklep.rest;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
import com.demo.sklep.model.Product;
import com.demo.sklep.photo.PhotoUtil;
import com.demo.sklep.repository.ProductRepository;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
@Path("/products")
@Produces("application/json")
@Consumes("application/json")
public class RProduct {
@Autowired
private ProductRepository productRepository;
@Autowired
private PhotoUtil photoUtil;
@GET
public List<Product> readAll() {
return productRepository.findAll();
}
@Path("/{id}")
@GET
public Product readOne(@PathParam("id") int id) {
return productRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
@Path("/{id}/price")
@GET
public BigDecimal getPrice(@PathParam("id") Integer id) {
Optional<Product> product = productRepository.findById(id);
if(product.isPresent()) {
return product.get().getPrice();
} else {
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
}
@Path("/{id}/price")
@PUT
public void setPrice(@PathParam("id") Integer id, BigDecimal newPrice) {
Optional<Product> product = productRepository.findById(id);
if(product.isPresent()) {
Product realProduct = product.get();
realProduct.setPrice(newPrice);
productRepository.save(realProduct);
} else {
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
}
@Path("/{id}")
@PUT
public void update(@PathParam("id") Integer productId, Product product) {
// Aby mieć pewność, że zapisujemu produkt o typ ID, wpisuję productId z URL-a.
// Ewentualnie możnaby jeszcze sprawdzić czy rekord istnieje, czy ID się zgadza
// i jeśli coś jest nie tak, to wyrzucić wyjątek.
product.setProductId(productId);
productRepository.save(product);
}
@POST
public Product insert(Product product) {
product.setProductId(null);
productRepository.save(product);
return product;
}
@Path("/{id}")
@DELETE
public void delete(@PathParam("id") Integer productId) {
try {
productRepository.deleteById(productId);
} catch (EmptyResultDataAccessException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Brak produktu nr " + productId);
}
}
@GET
@Path("/{id}/photo")
@Produces("image/jpeg")
public byte[] getPhoto(@PathParam("id") int productId) {
return photoUtil.readBytes(productId);
}
@PUT
@Path("/{id}/photo")
@Consumes("image/jpeg")
public void uploadPhoto(@PathParam("id") int productId, byte[] bytes) {
photoUtil.writeBytes(productId, bytes);
}
}
spring.datasource.url=jdbc:postgresql://localhost:5432/sklep
spring.datasource.username=kurs
spring.datasource.password=abc123
alx.photo_dir=/home/patryk/sklep/foto
package com.demo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class Pc37JerseyApplicationTests {
@Test
void contextLoads() {
}
}
target/
.project
.classpath
.settings/
*.iml
.idea/
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>pl.alx.kjava</groupId>
<artifactId>PC39-Wielomodulowy</artifactId>
<version>1.0</version>
</parent>
<artifactId>baza</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>[42.5, 43)</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package sklep.db;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Customer;
public class CustomerDAO {
private final DBConnection db;
CustomerDAO(DBConnection db) {
this.db = db;
}
public Customer findByEmail(String email) throws DBException, RecordNotFound {
final String sql = "SELECT * FROM customers WHERE customer_email = ?";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setString(1, email);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return customerFromRS(rs);
} else {
throw new RecordNotFound("Cannot find customer with email " + email);
}
}
} catch (SQLException e) {
throw new DBException("SQL error in CustomerDAO.findById: " + e.getMessage(), e);
}
}
public List<Customer> readAll() throws DBException {
final String sql = "SELECT * FROM customers";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
try (ResultSet rs = stmt.executeQuery()) {
return customerListFromRS(rs);
}
} catch (SQLException e) {
throw new DBException("SQL error in CustomerDAO.readAll: " + e.getMessage(), e);
}
}
private List<Customer> customerListFromRS(ResultSet rs) throws SQLException {
List<Customer> records = new ArrayList<>();
while (rs.next()) {
Customer product = customerFromRS(rs);
records.add(product);
}
return records;
}
private Customer customerFromRS(ResultSet rs) throws SQLException {
return new Customer(
rs.getString("customer_email"),
rs.getString("customer_name"),
rs.getString("phone_number"),
rs.getString("address"),
rs.getString("postal_code"),
rs.getString("city"));
}
public void insert(Customer customer) throws DBException {
// używać gdy obiekt ma wpisane ID (tu: email)
final String sql = "INSERT INTO customers("
+ "customer_email, customer_name, phone_number, address, postal_code, city)"
+ " VALUES (?, ?, ?, ?, ?, ?)";
try(PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setString(1, customer.getEmail());
stmt.setString(2, customer.getName());
stmt.setString(3, customer.getPhoneNumber());
stmt.setString(4, customer.getAddress());
stmt.setString(5, customer.getPostalCode());
stmt.setString(6, customer.getCity());
stmt.executeUpdate();
} catch (SQLException e) {
throw new DBException("Error during INSERT CUSTOMER", e);
}
}
public boolean update(Customer customer) throws DBException {
final String sql = "UPDATE customers SET "
+ " customer_name=?, phone_number=?, address=?, postal_code=?, city=?"
+ " WHERE customer_email = ?";
try(PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setString(1, customer.getName());
stmt.setString(2, customer.getPhoneNumber());
stmt.setString(3, customer.getAddress());
stmt.setString(4, customer.getPostalCode());
stmt.setString(5, customer.getCity());
stmt.setString(6, customer.getEmail());
int count = stmt.executeUpdate();
return count > 0;
} catch (SQLException e) {
throw new DBException("Error during UPDATE CUSTOMER", e);
}
}
public void save(Customer customer) throws DBException {
if(customer.getEmail() == null) {
throw new IllegalArgumentException("Customer email cannot be null");
} else {
if(! update(customer)) {
insert(customer);
}
}
}
public boolean delete(String email) throws DBException {
final String sql = "DELETE FROM customers WHERE customer_email = ?";
try(PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setString(1, email);
int count = stmt.executeUpdate();
return count > 0;
} catch (SQLException e) {
throw new DBException("Error during DELETE CUSTOMER", e);
}
}
public boolean delete(Customer customer) throws DBException {
return delete(customer.getEmail());
}
}
package sklep.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import sklep.exn.DBException;
public class DBConnection implements AutoCloseable {
private Connection sqlConnection;
private DBConnection(Connection sqlConnection) {
this.sqlConnection = sqlConnection;
}
public static DBConnection open() throws DBException {
return open(false);
}
public static DBConnection open(boolean autoCommit) throws DBException {
try {
Properties props = DBSettings.load();
if(props.containsKey("driver_class")) {
Class.forName(props.getProperty("driver_class"));
}
Connection c = DriverManager.getConnection(props.getProperty("url") , props);
c.setAutoCommit(autoCommit);
return new DBConnection(c);
} catch (ClassNotFoundException | SQLException e) {
throw new DBException("Cannot connect to postgresql: " + e, e);
}
}
public static DBConnection openLocalhost() throws DBException {
try {
Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/sklep", "kurs", "abc123");
c.setAutoCommit(false);
return new DBConnection(c);
} catch (SQLException e) {
throw new DBException("Cannot connect to postgresql: " + e, e);
}
}
@Override
public void close() {
try {
if (sqlConnection != null) {
sqlConnection.close();
sqlConnection = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public Connection getSqlConnection() {
return sqlConnection;
}
public void commit() throws DBException {
try {
sqlConnection.commit();
} catch (SQLException e) {
throw new DBException("Error during commit: " + e.getMessage(), e);
}
}
public void rollback() throws DBException {
try {
sqlConnection.rollback();
} catch (SQLException e) {
throw new DBException("Error during rollback: " + e.getMessage(), e);
}
}
public ProductDAO productDAO() {
return new ProductDAO(this);
}
public CustomerDAO customerDAO() {
return new CustomerDAO(this);
}
public OrderDAO orderDAO() {
return new OrderDAO(this);
}
}
package sklep.db;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import sklep.exn.DBException;
public class DBSettings {
public static final String DB_SETTINGS_SYSTEM_PROPERTY = "sklep.db_settings_location";
private static final String INTERNAL_DEFAULT_PROPERTIES = "/sklep.properties";
private static DBSettings dbSettings; // singleton
private final Properties props;
private DBSettings() throws DBException {
props = new Properties();
String systemProperty = System.getProperty(DB_SETTINGS_SYSTEM_PROPERTY);
try(InputStream input = systemProperty != null
? new FileInputStream(new File(systemProperty))
: DBSettings.class.getResourceAsStream(INTERNAL_DEFAULT_PROPERTIES) ) {
props.load(input);
} catch (IOException e) {
//e.printStackTrace();
throw new DBException("Cannot read settings. " + e, e);
}
}
public static synchronized DBSettings getInstance() throws DBException {
// Dla klasy typu "singleton" w aplikacji powstaje tylko jedna instancja (obiekt) tej klasy.
// Dostęp do tego obiektu odbywa się poprzez metodę statyczną taką jak ta.
// Tutaj mamy "leniwą inicjalizację", czyli obiekt jest tworzony przy pierwszej próbie dostepu.
if(dbSettings == null) {
dbSettings = new DBSettings();
}
return dbSettings;
}
public Properties getProperties() {
return props;
}
public static Properties load() throws DBException {
return getInstance().getProperties();
}
}
package sklep.db;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Order;
import sklep.model.OrderProduct;
public class OrderDAO {
private static final String[] ID_COLUMNS = {"order_id", "order_date"};
private final DBConnection db;
OrderDAO(DBConnection db) {
this.db = db;
}
public Order findById(int orderId) throws DBException, RecordNotFound {
final String sql = "SELECT * FROM orders WHERE order_id = ?";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setInt(1, orderId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return orderFromRS(rs);
} else {
throw new RecordNotFound("Cannot find order with id " + orderId);
}
}
} catch (SQLException e) {
throw new DBException("SQL error in OrderDAO.findById: " + e.getMessage(), e);
}
}
public List<Order> readAll() throws DBException {
final String sql = "SELECT * FROM orders ORDER BY order_id";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
try (ResultSet rs = stmt.executeQuery()) {
return orderListFromRS(rs);
}
} catch (SQLException e) {
throw new DBException("SQL error in OrderDAO.readAll: " + e.getMessage(), e);
}
}
public List<Order> customerOrders(String email) throws DBException {
final String sql = "SELECT * FROM orders WHERE customer_email = ? ORDER BY order_id";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setString(1, email);
try (ResultSet rs = stmt.executeQuery()) {
return orderListFromRS(rs);
}
} catch (SQLException e) {
throw new DBException("SQL error in OrderDAO.customerOrders: " + e.getMessage(), e);
}
}
private List<Order> orderListFromRS(ResultSet rs) throws SQLException, DBException {
List<Order> orders = new ArrayList<>();
while (rs.next()) {
Order order = orderFromRS(rs);
orders.add(order);
}
return orders;
}
private Order orderFromRS(ResultSet rs) throws SQLException, DBException {
Order order = Order.ofDbFields(
rs.getInt("order_id"),
rs.getString("customer_email"),
rs.getTimestamp("order_date"),
rs.getString("status"));
order.addProducts(orderProductsForOrder(order.getOrderId()));
return order;
}
List<OrderProduct> orderProductsForOrder(int orderId) throws DBException {
final String sql = "SELECT * FROM order_products WHERE order_id = ? ORDER BY product_id";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setInt(1, orderId);
try (ResultSet rs = stmt.executeQuery()) {
List<OrderProduct> ops = new ArrayList<>();
while(rs.next()) {
ops.add(orderProductFromRS(rs));
}
return ops;
}
} catch (SQLException e) {
throw new DBException("SQL error in OrderDAO.customerOrders: " + e.getMessage(), e);
}
}
private OrderProduct orderProductFromRS(ResultSet rs) throws SQLException {
return new OrderProduct(rs.getInt("order_id"), rs.getInt("product_id"), rs.getInt("quantity"), rs.getBigDecimal("actual_price"));
}
}
package sklep.db;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
/* DAO - Data Access Object(s)
Dostęp do baz danych oparty o takie zasady:
tabelom bazodanowym odpowiadają klasy w naszej aplikacji
np. dla tabeli products mamy klasę Product (w pakiecie model)
dla takie pary tabela products + klasa Product tworzymy klasę narzędziową ProductDAO , której zadaniem jest obsługa tej tabeli: odczyt, zapis, wyszukiwanie, i inne operacje jeśli są potrzebne.
*/
public class ProductDAO {
private static final BigDecimal MAX_PRICE = new BigDecimal(1_000_000_000);
private static final String[] ID_COLUMNS = {"product_id"};
private final DBConnection db;
ProductDAO(DBConnection db) {
this.db = db;
}
public Product findById(int productId) throws DBException, RecordNotFound {
final String sql = "SELECT * FROM products WHERE product_id = ?";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setInt(1, productId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return productFromRS(rs);
} else {
throw new RecordNotFound("Cannot find product with id " + productId);
}
}
} catch (SQLException e) {
throw new DBException("SQL error in ProductDAO.findById: " + e.getMessage(), e);
}
}
public List<Product> readAll() throws DBException {
final String sql = "SELECT * FROM products ORDER BY product_id";
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
try (ResultSet rs = stmt.executeQuery()) {
return productListFromRS(rs);
}
} catch (SQLException e) {
throw new DBException("SQL error in ProductDAO.readAll: " + e.getMessage(), e);
}
}
public List<Product> findByPrice(BigDecimal minPrice, BigDecimal maxPrice) throws DBException {
final String sql = "SELECT * FROM products WHERE price BETWEEN ? AND ? ORDER BY product_id";
if(minPrice == null)
minPrice = BigDecimal.ZERO;
if(maxPrice == null)
maxPrice = MAX_PRICE;
try (PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setBigDecimal(1, minPrice);
stmt.setBigDecimal(2, maxPrice);
try (ResultSet rs = stmt.executeQuery()) {
return productListFromRS(rs);
}
} catch (SQLException e) {
throw new DBException("SQL error in ProductDAO.findByPrice: " + e.getMessage(), e);
}
}
private List<Product> productListFromRS(ResultSet rs) throws SQLException {
List<Product> products = new ArrayList<>();
while (rs.next()) {
Product product = productFromRS(rs);
products.add(product);
}
return products;
}
private Product productFromRS(ResultSet rs) throws SQLException {
return new Product(
rs.getInt("product_id"),
rs.getString("product_name"),
rs.getBigDecimal("price"),
rs.getBigDecimal("vat"),
rs.getString("description"));
}
public void insert(Product product) throws DBException {
// używać gdy obiekt ma wpisane ID
final String sql = "INSERT INTO products("
+ " product_id, product_name, price, vat, description)"
+ " VALUES (?, ?, ?, ?, ?)";
try(PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setInt(1, product.getProductId());
stmt.setString(2, product.getProductName());
stmt.setBigDecimal(3, product.getPrice());
stmt.setBigDecimal(4, product.getVat());
stmt.setString(5, product.getDescription());
stmt.executeUpdate();
} catch (SQLException e) {
throw new DBException("Error during INSERT PRODUCT", e);
}
}
public void insertNew(Product product) throws DBException {
// używać gdy obiekt nie ma wpisanego ID (productID == null)
final String sql = "INSERT INTO products("
+ " product_name, price, vat, description)"
+ " VALUES (?, ?, ?, ?)";
try(PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql, ID_COLUMNS)) {
stmt.setString(1, product.getProductName());
stmt.setBigDecimal(2, product.getPrice());
stmt.setBigDecimal(3, product.getVat());
stmt.setString(4, product.getDescription());
stmt.executeUpdate();
try (ResultSet rs = stmt.getGeneratedKeys()) {
if(rs.next()) {
// w obiekcie, który mamy w pamięci, uzupełniamy brakujące ID na podstawie tego, co wygenerowała baza
product.setProductId(rs.getInt(1));
}
};
} catch (SQLException e) {
throw new DBException("Error during INSERT PRODUCT", e);
}
}
public boolean update(Product product) throws DBException {
final String sql = "UPDATE products SET "
+ " product_name = ?, price = ?, vat = ?, description = ?"
+ " WHERE product_id = ?";
try(PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setString(1, product.getProductName());
stmt.setBigDecimal(2, product.getPrice());
stmt.setBigDecimal(3, product.getVat());
stmt.setString(4, product.getDescription());
stmt.setInt(5, product.getProductId());
int count = stmt.executeUpdate();
return count > 0;
} catch (SQLException e) {
throw new DBException("Error during UPDATE PRODUCT", e);
}
}
public void save(Product product) throws DBException {
if(product.getProductId() == null) {
// wstawiamy nowy rekord korzystajac z sekwecji
insertNew(product);
} else {
if(! update(product)) {
insert(product);
}
}
}
public boolean delete(int productId) throws DBException {
final String sql = "DELETE FROM products WHERE product_id = ?";
try(PreparedStatement stmt = db.getSqlConnection().prepareStatement(sql)) {
stmt.setInt(1, productId);
int count = stmt.executeUpdate();
return count > 0;
} catch (SQLException e) {
throw new DBException("Error during DELETE PRODUCT", e);
}
}
public boolean delete(Product product) throws DBException {
return delete(product.getProductId());
}
}
package sklep.db;
import java.util.List;
import sklep.exn.DBException;
import sklep.model.Product;
public class ZwyklyOdczyt_DAO {
public static void main(String[] args) {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
List<Product> products = productDAO.readAll();
for (Product product : products) {
System.out.println(product);
}
} catch (DBException e) {
e.printStackTrace();
}
}
}
package sklep.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class ZwyklyOdczyt_JDBC {
public static void main(String[] args) {
String url = "jdbc:postgresql://localhost:5432/sklep";
String sql = "SELECT * FROM products";
try(Connection c = DriverManager.getConnection(url, "kurs", "abc123");
PreparedStatement stmt= c.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
while(rs.next()) {
System.out.printf("%d: %s za cenę %s\n",
rs.getInt("product_id"), rs.getString("product_name"), rs.getBigDecimal("price"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
package sklep.photo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import sklep.db.DBSettings;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
public class PhotoUtil {
private static final String EXT = ".jpg";
public static File getFile(int productId) throws DBException, RecordNotFound {
Path path = getPath(productId);
File file = path.toFile();
if(file.exists()) {
return file;
} else {
throw new RecordNotFound("Cannot read photo for product id = " + productId);
}
}
public static byte[] readBytes(int productId) throws DBException, RecordNotFound {
Path path = getPath(productId);
try {
return Files.readAllBytes(path);
} catch (IOException e) {
// System.err.println(e);
throw new RecordNotFound("Cannot read photo for product id = " + productId);
}
}
public static void writeStream(int productId, InputStream inputStream) {
try {
Path path = getPath(productId);
Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
// wypisujemy błąd, ale metoda kończy się normalnie
e.printStackTrace();
}
}
private static Path getPath(int productId) throws DBException {
String dir = DBSettings.load().getProperty("photo_dir");
String fileName = productId + EXT;
return Paths.get(dir, fileName);
}
}
url=jdbc:postgresql://localhost:5432/sklep
driver_class=org.postgresql.Driver
user=kurs
password=abc123
photo_dir=/home/patryk/sklep/foto
# Sciezki na Windows: albo piszemy slashe / , albo podwojne backslashe \\
# photo_dir=C:/Users/Patryk/Desktop/sklep/foto
# photo_dir=C:\\Users\\Patryk\\Desktop\\sklep\\foto
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>pl.alx.kjava</groupId>
<artifactId>PC39-Wielomodulowy</artifactId>
<version>1.0</version>
</parent>
<artifactId>model</artifactId>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<!-- wersja przyjdzie z parenta, podobnie jak w Spring Boot -->
</dependency>
</dependencies>
</project>
\ No newline at end of file
package sklep.exn;
public class DBException extends SklepException {
public DBException() {
super();
}
public DBException(String message, Throwable cause) {
super(message, cause);
}
public DBException(String message) {
super(message);
}
}
package sklep.exn;
public class RecordNotFound extends SklepException {
public RecordNotFound() {
super();
}
public RecordNotFound(String message) {
super(message);
}
}
package sklep.exn;
public class SklepException extends Exception {
public SklepException() {
super();
}
public SklepException(String message, Throwable cause) {
super(message, cause);
}
public SklepException(String message) {
super(message);
}
public SklepException(Throwable cause) {
super(cause);
}
}
package sklep.model;
import java.util.Objects;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Customer {
@XmlAttribute
private String email;
private String name;
@XmlElement(name="phone")
private String phoneNumber;
private String address;
@XmlElement(name="postal-code")
private String postalCode;
private String city;
public Customer() {
}
public Customer(String email, String name, String phone, String address, String postalCode, String city) {
this.email = email;
this.name = name;
this.phoneNumber = phone;
this.address = address;
this.postalCode = postalCode;
this.city = city;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phone) {
this.phoneNumber = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Customer [email=" + email + ", name=" + name + ", phone=" + phoneNumber + ", address=" + address
+ ", postalCode=" + postalCode + ", city=" + city + "]";
}
@Override
public int hashCode() {
return Objects.hash(email, name, address, city, phoneNumber, postalCode);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Customer other = (Customer) obj;
return Objects.equals(email, other.email) && Objects.equals(name, other.name)
&& Objects.equals(address, other.address) && Objects.equals(city, other.city)
&& Objects.equals(phoneNumber, other.phoneNumber) && Objects.equals(postalCode, other.postalCode);
}
}
package sklep.model;
import java.time.LocalDateTime;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
@Override
public LocalDateTime unmarshal(String s) throws Exception {
return LocalDateTime.parse(s);
}
@Override
public String marshal(LocalDateTime dt) throws Exception {
return dt.toString();
}
}
package sklep.model;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementWrapper;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Order {
@XmlAttribute(name="id")
private Integer orderId;
@XmlAttribute(name="customer-email")
private String customerEmail;
@XmlElement(name="order-date")
@XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
private LocalDateTime orderDate;
@XmlAttribute(name="status")
private Status orderStatus;
@XmlElementWrapper(name="products")
@XmlElement(name="product")
public final List<OrderProduct> products = new ArrayList<>();
public Order() {
}
public Order(Integer orderId, String customerEmail, LocalDateTime orderDate, Status orderStatus) {
this.orderId = orderId;
this.customerEmail = customerEmail;
this.orderDate = orderDate;
this.orderStatus = orderStatus;
}
public static Order ofDbFields(int orderId, String customerEmail, java.sql.Timestamp orderDate, String orderStatus) {
return new Order(orderId, customerEmail,
orderDate.toLocalDateTime(),
Status.valueOf(orderStatus.toUpperCase()));
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getCustomerEmail() {
return customerEmail;
}
public void setCustomerEmail(String customerEmail) {
this.customerEmail = customerEmail;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public void setOrderDate(LocalDateTime orderDate) {
this.orderDate = orderDate;
}
public Status getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(Status orderStatus) {
this.orderStatus = orderStatus;
}
public List<OrderProduct> getProducts() {
return Collections.unmodifiableList(products);
}
public void addProduct(OrderProduct product) {
this.products.add(product);
}
public void addProducts(Collection<OrderProduct> products) {
this.products.addAll(products);
}
public void setProducts(Collection<OrderProduct> products) {
this.products.clear();
this.products.addAll(products);
}
@Override
public String toString() {
return "Order [orderId=" + orderId + ", customerEmail=" + customerEmail + ", orderDate=" + orderDate
+ ", orderStatus=" + orderStatus + "]";
}
@Override
public int hashCode() {
return Objects.hash(customerEmail, orderDate, orderId, orderStatus);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Order other = (Order) obj;
return Objects.equals(customerEmail, other.customerEmail) && Objects.equals(orderDate, other.orderDate)
&& Objects.equals(orderId, other.orderId) && orderStatus == other.orderStatus;
}
public enum Status {
NEW,
CONFIRMED,
PAID,
SHIPPED,
CLOSED,
RETURNED,
;
}
}
package sklep.model;
import java.math.BigDecimal;
import java.util.Objects;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlTransient;
public class OrderProduct {
@XmlTransient
private Integer orderId;
@XmlAttribute(name="id")
private Integer productId;
private int quantity;
@XmlElement(name="actual-price")
private BigDecimal actualPrice;
public OrderProduct() {
}
public OrderProduct(Integer orderId, Integer productId, int quantity, BigDecimal actualPrice) {
this.orderId = orderId;
this.productId = productId;
this.quantity = quantity;
this.actualPrice = actualPrice;
}
public static OrderProduct of(Integer orderId, Product product, int quantity) {
return new OrderProduct(orderId, product.getProductId(), quantity, product.getPrice());
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public BigDecimal getActualPrice() {
return actualPrice;
}
public void setActualPrice(BigDecimal actualPrice) {
this.actualPrice = actualPrice;
}
@Override
public String toString() {
return "OrderProduct [orderId=" + orderId + ", productId=" + productId + ", quantity=" + quantity
+ ", actualPrice=" + actualPrice+ "]";
}
@Override
public int hashCode() {
return Objects.hash(orderId, productId, quantity, actualPrice);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
OrderProduct other = (OrderProduct) obj;
return Objects.equals(orderId, other.orderId) && Objects.equals(productId, other.productId)
&& quantity == other.quantity
&& Objects.equals(actualPrice, other.actualPrice);
}
}
package sklep.model;
import java.math.BigDecimal;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlValue;
@XmlRootElement
public class Price {
@XmlValue
private BigDecimal value;
public Price() {
this.value = BigDecimal.ZERO;
}
public Price(BigDecimal value) {
this.value = value;
}
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
}
@Override
public String toString() {
return value.toString();
}
}
package sklep.model;
import java.math.BigDecimal;
import java.util.Objects;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Product {
@XmlAttribute(name="id")
private Integer productId;
@XmlElement(name="product-name")
private String productName;
private BigDecimal price;
private BigDecimal vat;
private String description;
public Product() {
}
public Product(Integer productId, String productName, BigDecimal price, BigDecimal vat, String description) {
this.productId = productId;
this.productName = productName;
this.price = price;
this.vat = vat;
this.description = description;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getVat() {
return vat;
}
public void setVat(BigDecimal vat) {
this.vat = vat;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public int hashCode() {
return Objects.hash(description, price, vat, productId, productName);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Product other = (Product) obj;
return Objects.equals(productId, other.productId) && Objects.equals(productName, other.productName)
&& Objects.equals(price, other.price)
&& Objects.equals(vat, other.vat)
&& Objects.equals(description, other.description);
}
@Override
public String toString() {
return "Product [productId=" + productId + ", productName=" + productName + ", price=" + price + ", vat=" + vat
+ ", description=" + description + "]";
}
public String toHtml() {
return String.format("<div class='product'>"
+ "<h2>%s</h2>"
+ "<p>(nr %d)</p>"
+ "<p>Cena: <strong>%,.2f PLN</strong></p>"
+ "<p>%s</p>"
+ "</div>",
getProductName(),
getProductId(),
getPrice(),
getDescription());
}
}
package sklep.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="products")
public class ProductList {
@XmlElement(name="product")
private final List<Product> products = new ArrayList<>();
public ProductList() {
// zostawia pustą listę
}
public ProductList(Collection<Product> products) {
this.products.addAll(products);
}
public List<Product> getProducts() {
return Collections.unmodifiableList(this.products);
}
public void setProducts(Collection<Product> products) {
this.products.clear();
this.products.addAll(products);
}
@Override
public String toString() {
return this.products.toString();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment