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/
employee_id;first_name;last_name;job_title;salary;hire_date;department_name;address;postal_code;city;country
100;Steven;King;President;24000;1997-06-17;Executive;2004 Charade Rd;98199;Seattle;United States of America
101;Neena;Kochhar;Administration Vice President;17000;1999-09-21;Executive;2004 Charade Rd;98199;Seattle;United States of America
102;Lex;De Haan;Administration Vice President;17000;2003-01-13;Executive;2004 Charade Rd;98199;Seattle;United States of America
103;Alexander;Hunold;Programmer;9000;2000-01-03;IT;2014 Jabberwocky Rd;26192;Southlake;United States of America
104;Bruce;Ernst;Programmer;6000;2001-05-21;IT;2014 Jabberwocky Rd;26192;Southlake;United States of America
105;David;Austin;Programmer;4800;2007-06-25;IT;2014 Jabberwocky Rd;26192;Southlake;United States of America
106;Valli;Pataballa;Programmer;4800;2008-02-05;IT;2014 Jabberwocky Rd;26192;Southlake;United States of America
107;Diana;Lorentz;Programmer;4200;2009-02-07;IT;2014 Jabberwocky Rd;26192;Southlake;United States of America
108;Nancy;Greenberg;Finance Manager;12000;2004-08-17;Finance;2004 Charade Rd;98199;Seattle;United States of America
109;Daniel;Faviet;Accountant;9000;2004-08-16;Finance;2004 Charade Rd;98199;Seattle;United States of America
110;John;Chen;Accountant;8200;2007-09-28;Finance;2004 Charade Rd;98199;Seattle;United States of America
111;Ismael;Sciarra;Accountant;7700;2007-09-30;Finance;2004 Charade Rd;98199;Seattle;United States of America
112;Jose Manuel;Urman;Accountant;7800;1998-03-07;Finance;2004 Charade Rd;98199;Seattle;United States of America
113;Luis;Popp;Accountant;6900;2009-12-07;Finance;2004 Charade Rd;98199;Seattle;United States of America
114;Den;Raphaely;Purchasing Manager;11000;2004-12-07;Purchasing;2004 Charade Rd;98199;Seattle;United States of America
115;Alexander;Khoo;Purchasing Clerk;3100;2005-05-18;Purchasing;2004 Charade Rd;98199;Seattle;United States of America
116;Shelli;Baida;Purchasing Clerk;2900;2007-12-24;Purchasing;2004 Charade Rd;98199;Seattle;United States of America
117;Sigal;Tobias;Purchasing Clerk;2800;2007-07-24;Purchasing;2004 Charade Rd;98199;Seattle;United States of America
118;Guy;Himuro;Purchasing Clerk;2600;2008-11-15;Purchasing;2004 Charade Rd;98199;Seattle;United States of America
119;Karen;Colmenares;Purchasing Clerk;2500;2009-08-10;Purchasing;2004 Charade Rd;98199;Seattle;United States of America
120;Matthew;Weiss;Stock Manager;8000;2006-07-18;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
121;Adam;Fripp;Stock Manager;8200;2007-04-10;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
122;Payam;Kaufling;Stock Manager;7900;2005-05-01;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
123;Shanta;Vollman;Stock Manager;6500;2007-10-10;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
124;Kevin;Mourgos;Stock Manager;5800;2009-11-16;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
125;Julia;Nayer;Stock Clerk;3200;2007-07-16;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
126;Irene;Mikkilineni;Stock Clerk;2700;2008-09-28;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
127;James;Landry;Stock Clerk;2400;2009-01-14;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
128;Steven;Markle;Stock Clerk;2200;2010-03-08;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
129;Laura;Bissot;Stock Clerk;3300;2007-08-20;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
130;Mozhe;Atkinson;Stock Clerk;2800;2007-10-30;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
131;James;Marlow;Stock Clerk;2500;2007-02-16;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
132;TJ;Olson;Stock Clerk;2100;2009-04-10;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
133;Jason;Mallin;Stock Clerk;3300;2006-06-14;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
134;Michael;Rogers;Stock Clerk;2900;2008-08-26;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
135;Ki;Gee;Stock Clerk;2400;2009-12-12;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
136;Hazel;Philtanker;Stock Clerk;2200;2011-02-06;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
137;Renske;Ladwig;Stock Clerk;3600;2005-07-14;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
138;Stephen;Stiles;Stock Clerk;3200;2007-10-26;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
139;John;Seo;Stock Clerk;2700;2008-02-12;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
140;Joshua;Patel;Stock Clerk;2500;2008-04-06;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
141;Trenna;Rajs;Stock Clerk;3500;2005-10-17;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
142;Curtis;Davies;Stock Clerk;3100;2007-01-29;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
143;Randall;Matos;Stock Clerk;2600;2008-03-15;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
144;Peter;Vargas;Stock Clerk;2500;2008-07-09;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
145;John;Russell;Sales Manager;14000;2006-10-01;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
146;Karen;Partners;Sales Manager;13500;2007-01-05;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
147;Alberto;Errazuriz;Sales Manager;12000;2007-03-10;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
148;Gerald;Cambrault;Sales Manager;11000;2009-10-15;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
149;Eleni;Zlotkey;Sales Manager;10500;2000-01-29;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
150;Peter;Tucker;Sales Representative;10000;2007-01-30;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
151;David;Bernstein;Sales Representative;9500;2007-03-24;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
152;Peter;Hall;Sales Representative;9000;2007-08-20;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
153;Christopher;Olsen;Sales Representative;8000;2008-03-30;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
154;Nanette;Cambrault;Sales Representative;7500;2008-12-09;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
155;Oliver;Tuvault;Sales Representative;7000;2009-11-23;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
156;Janette;King;Sales Representative;10000;2006-01-30;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
157;Patrick;Sully;Sales Representative;9500;2006-03-04;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
158;Allan;McEwen;Sales Representative;9000;2006-08-01;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
159;Lindsey;Smith;Sales Representative;8000;2007-03-10;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
160;Louise;Doran;Sales Representative;7500;2007-12-15;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
161;Sarath;Sewall;Sales Representative;7000;2008-11-03;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
162;Clara;Vishney;Sales Representative;10500;2007-11-11;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
163;Danielle;Greene;Sales Representative;9500;2009-03-19;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
164;Mattea;Marvins;Sales Representative;7200;2010-01-24;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
165;David;Lee;Sales Representative;6800;2000-02-23;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
166;Sundar;Ande;Sales Representative;6400;2000-03-24;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
167;Amit;Banda;Sales Representative;6200;2000-04-21;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
168;Lisa;Ozer;Sales Representative;11500;2007-03-11;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
169;Harrison;Bloom;Sales Representative;10000;2008-03-23;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
170;Tayler;Fox;Sales Representative;9600;2008-01-24;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
171;William;Smith;Sales Representative;7400;2009-02-23;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
172;Elizabeth;Bates;Sales Representative;7300;2009-03-24;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
173;Sundita;Kumar;Sales Representative;6100;2010-04-21;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
174;Ellen;Abel;Sales Representative;11000;2006-05-11;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
175;Alyssa;Hutton;Sales Representative;8800;2007-03-19;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
176;Jonathon;Taylor;Sales Representative;8600;2008-03-24;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
177;Jack;Livingston;Sales Representative;8400;2008-04-23;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
178;Kimberely;Grant;Sales Representative;7000;2009-05-24;;;;;
179;Charles;Johnson;Sales Representative;6200;2011-01-04;Sales;Magdalen Centre, The Oxford Science Park;OX9 9ZB;Oxford;United Kingdom
180;Winston;Taylor;Shipping Clerk;3200;2008-01-24;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
181;Jean;Fleaur;Shipping Clerk;3100;2008-02-23;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
182;Martha;Sullivan;Shipping Clerk;2500;2009-06-21;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
183;Girard;Geoni;Shipping Clerk;2800;2000-02-03;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
184;Nandita;Sarchand;Shipping Clerk;4200;2006-01-27;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
185;Alexis;Bull;Shipping Clerk;4100;2007-02-20;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
186;Julia;Dellinger;Shipping Clerk;3400;2008-06-24;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
187;Anthony;Cabrio;Shipping Clerk;3000;2009-02-07;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
188;Kelly;Chung;Shipping Clerk;3800;2007-06-14;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
189;Jennifer;Dilly;Shipping Clerk;3600;2007-08-13;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
190;Timothy;Gates;Shipping Clerk;2900;2008-07-11;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
191;Randall;Perkins;Shipping Clerk;2500;2009-12-19;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
192;Sarah;Bell;Shipping Clerk;4000;2006-02-04;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
193;Britney;Everett;Shipping Clerk;3900;2007-03-03;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
194;Samuel;McCain;Shipping Clerk;3200;2008-07-01;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
195;Vance;Jones;Shipping Clerk;2800;2009-03-17;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
196;Alana;Walsh;Shipping Clerk;3100;2008-04-24;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
197;Kevin;Feeney;Shipping Clerk;3000;2008-05-23;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
198;Donald;OConnell;Shipping Clerk;2600;2009-06-21;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
199;Douglas;Grant;Shipping Clerk;2600;2010-01-13;Shipping;2011 Interiors Blvd;99236;South San Francisco;United States of America
200;Jennifer;Whalen;Administration Assistant;4400;1987-09-17;Administration;2004 Charade Rd;98199;Seattle;United States of America
201;Michael;Hartstein;Marketing Manager;13000;2006-02-17;Marketing;147 Spadina Ave;M5V 2L7;Toronto;Canada
202;Pat;Fay;Marketing Representative;6000;2007-08-17;Marketing;147 Spadina Ave;M5V 2L7;Toronto;Canada
203;Susan;Mavris;Human Resources Representative;6500;2004-06-07;Human Resources;8204 Arthur St;;London;United Kingdom
204;Hermann;Baer;Public Relations Representative;10000;2004-06-07;Public Relations;Schwanthalerstr. 7031;80925;Munich;Germany
205;Shelley;Higgins;Accounting Manager;12000;2004-06-07;Accounting;2004 Charade Rd;98199;Seattle;United States of America
206;William;Gietz;Public Accountant;8300;2004-06-07;Accounting;2004 Charade Rd;98199;Seattle;United States of America
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.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.time.LocalDate;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class PrzelicznikWalutOkno {
private static final Font FONT_LABEL = new Font("Times New Roman", Font.PLAIN, 24);
private static final Font FONT_WARTOSC = new Font("Dialog", Font.BOLD, 24);
private static final Font FONT_TXT = new Font("Dialog", Font.BOLD, 32);
private static final Font FONT_WYNIK = new Font("Dialog", Font.BOLD, 32);
private static final Font FONT_BUTTON = new Font("Dialog", Font.BOLD, 28);
private Tabela tabela = null;
private JFrame frmPrzelicznikWalut;
private JTextField txtKwota;
private JComboBox<String> comboBox_Waluta;
private JLabel lbl_KodWaluty;
private JLabel lbl_NazwaWaluty;
private JLabel lbl_KursWaluty;
private JRadioButton rdbtnWalutaNaZlote;
private JRadioButton rdbtnZloteNaWalute;
private JButton btnPrzelicz;
private JLabel lblPrawdziwyWynik;
private final ButtonGroup buttonGroup = new ButtonGroup();
private JPanel panel_1;
private JLabel lblTabela;
private JLabel lbl_NumerTabeli;
private JLabel lblData;
private JLabel lbl_DataTabeli;
private JTextField txtData;
private JButton btnPobierzAktualne;
private Pobieranie pobieranie = Pobieranie.utworz("xml");
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
// com.formdev.flatlaf.FlatDarkLaf.setup();
PrzelicznikWalutOkno window = new PrzelicznikWalutOkno();
window.txtData.setText(LocalDate.now().toString());
window.frmPrzelicznikWalut.setVisible(true);
window.pobierzAktualneKursy();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public PrzelicznikWalutOkno() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frmPrzelicznikWalut = new JFrame();
frmPrzelicznikWalut.setTitle("Przelicznik Walut");
frmPrzelicznikWalut.setBounds(100, 100, 480, 701);
frmPrzelicznikWalut.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel lblWybierzWalut = new JLabel("Wybierz walutę");
lblWybierzWalut.setFont(FONT_LABEL);
comboBox_Waluta = new JComboBox<>();
comboBox_Waluta.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
wyswietlDaneWaluty();
}
});
comboBox_Waluta.setModel(
new DefaultComboBoxModel<>(new String[] {"--"}));
comboBox_Waluta.setFont(FONT_WARTOSC);
JPanel panel = new JPanel();
txtKwota = new JTextField();
txtKwota.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
przelicz();
}
});
txtKwota.setText("100");
txtKwota.setColumns(10);
txtKwota.setFont(FONT_TXT);
JLabel lblPodajKwot = new JLabel("Podaj kwotę");
lblPodajKwot.setFont(FONT_LABEL);
rdbtnWalutaNaZlote = new JRadioButton("waluta na złote");
rdbtnWalutaNaZlote.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
przelicz();
}
});
rdbtnWalutaNaZlote.setFont(FONT_LABEL);
buttonGroup.add(rdbtnWalutaNaZlote);
rdbtnZloteNaWalute = new JRadioButton("złote na walute");
rdbtnZloteNaWalute.setFont(FONT_LABEL);
rdbtnZloteNaWalute.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
przelicz();
}
});
buttonGroup.add(rdbtnZloteNaWalute);
btnPrzelicz = new JButton("Przelicz");
btnPrzelicz.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
przelicz();
}
});
btnPrzelicz.setFont(FONT_BUTTON);
JLabel lblWynik = new JLabel("Wynik");
lblWynik.setFont(FONT_LABEL);
lblPrawdziwyWynik = new JLabel("0.00");
lblPrawdziwyWynik.setFont(FONT_WYNIK);
lblPrawdziwyWynik.setForeground(Color.GRAY);
panel_1 = new JPanel();
JLabel lblPodajDat = new JLabel("Podaj datę");
lblPodajDat.setFont(FONT_LABEL);
txtData = new JTextField();
txtData.setHorizontalAlignment(SwingConstants.CENTER);
txtData.setFont(new Font("Dialog", Font.PLAIN, 20));
txtData.setText("data");
txtData.setColumns(10);
JButton btnZmieDat = new JButton("Zmień datę");
btnZmieDat.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
pobierzKursyDlaDaty();
}
});
btnZmieDat.setFont(new Font("Dialog", Font.BOLD, 18));
btnPobierzAktualne = new JButton("Pobierz aktualne");
btnPobierzAktualne.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
pobierzAktualneKursy();
}
});
btnPobierzAktualne.setFont(new Font("Dialog", Font.BOLD, 18));
GroupLayout groupLayout = new GroupLayout(frmPrzelicznikWalut.getContentPane());
groupLayout.setHorizontalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGroup(groupLayout.createSequentialGroup()
.addContainerGap()
.addGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
.addComponent(panel_1, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 448, Short.MAX_VALUE)
.addComponent(btnPrzelicz, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 448, Short.MAX_VALUE)
.addGroup(Alignment.LEADING, groupLayout.createSequentialGroup()
.addComponent(rdbtnWalutaNaZlote)
.addPreferredGap(ComponentPlacement.RELATED, 108, Short.MAX_VALUE)
.addComponent(rdbtnZloteNaWalute))
.addGroup(Alignment.LEADING, groupLayout.createSequentialGroup()
.addComponent(lblPodajKwot)
.addPreferredGap(ComponentPlacement.RELATED, 139, Short.MAX_VALUE)
.addComponent(txtKwota, GroupLayout.PREFERRED_SIZE, 189, GroupLayout.PREFERRED_SIZE))
.addComponent(panel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 448, Short.MAX_VALUE)
.addComponent(lblWynik, Alignment.LEADING)
.addComponent(lblPrawdziwyWynik, Alignment.LEADING)
.addGroup(Alignment.LEADING, groupLayout.createSequentialGroup()
.addGroup(groupLayout.createParallelGroup(Alignment.TRAILING, false)
.addComponent(lblPodajDat, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lblWybierzWalut, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 223, Short.MAX_VALUE))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
.addComponent(txtData, GroupLayout.DEFAULT_SIZE, 207, Short.MAX_VALUE)
.addComponent(comboBox_Waluta, 0, 207, Short.MAX_VALUE)))
.addGroup(groupLayout.createSequentialGroup()
.addComponent(btnPobierzAktualne, GroupLayout.PREFERRED_SIZE, 207, GroupLayout.PREFERRED_SIZE)
.addGap(34)
.addComponent(btnZmieDat, GroupLayout.DEFAULT_SIZE, 207, Short.MAX_VALUE)))
.addContainerGap())
);
groupLayout.setVerticalGroup(
groupLayout.createParallelGroup(Alignment.TRAILING)
.addGroup(groupLayout.createSequentialGroup()
.addContainerGap()
.addComponent(panel_1, GroupLayout.PREFERRED_SIZE, 103, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(groupLayout.createParallelGroup(Alignment.LEADING, false)
.addComponent(txtData, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(lblPodajDat, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(ComponentPlacement.RELATED)
.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
.addComponent(btnZmieDat)
.addComponent(btnPobierzAktualne, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.RELATED, 17, Short.MAX_VALUE)
.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
.addComponent(comboBox_Waluta, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(lblWybierzWalut))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addComponent(panel, GroupLayout.PREFERRED_SIZE, 142, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
.addComponent(txtKwota, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(lblPodajKwot))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
.addComponent(rdbtnWalutaNaZlote)
.addComponent(rdbtnZloteNaWalute))
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btnPrzelicz)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addComponent(lblWynik)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addComponent(lblPrawdziwyWynik)
.addGap(33))
);
lblTabela = new JLabel("Tabela");
lblTabela.setFont(FONT_LABEL);
lbl_NumerTabeli = new JLabel("?");
lbl_NumerTabeli.setFont(new Font("Dialog", Font.BOLD, 24));
lblData = new JLabel("Data:");
lblData.setFont(FONT_LABEL);
lbl_DataTabeli = new JLabel("?");
lbl_DataTabeli.setFont(new Font("Dialog", Font.BOLD, 24));
GroupLayout gl_panel_1 = new GroupLayout(panel_1);
gl_panel_1.setHorizontalGroup(
gl_panel_1.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel_1.createSequentialGroup()
.addContainerGap()
.addGroup(gl_panel_1.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel_1.createSequentialGroup()
.addComponent(lblTabela, GroupLayout.PREFERRED_SIZE, 95, GroupLayout.PREFERRED_SIZE)
.addGap(18)
.addComponent(lbl_NumerTabeli, GroupLayout.PREFERRED_SIZE, 311, GroupLayout.PREFERRED_SIZE))
.addGroup(gl_panel_1.createSequentialGroup()
.addComponent(lblData, GroupLayout.PREFERRED_SIZE, 95, GroupLayout.PREFERRED_SIZE)
.addGap(18)
.addComponent(lbl_DataTabeli, GroupLayout.PREFERRED_SIZE, 311, GroupLayout.PREFERRED_SIZE)))
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
gl_panel_1.setVerticalGroup(
gl_panel_1.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel_1.createSequentialGroup()
.addContainerGap()
.addGroup(gl_panel_1.createParallelGroup(Alignment.LEADING)
.addComponent(lblTabela, GroupLayout.PREFERRED_SIZE, 29, GroupLayout.PREFERRED_SIZE)
.addComponent(lbl_NumerTabeli, GroupLayout.PREFERRED_SIZE, 29, GroupLayout.PREFERRED_SIZE))
.addGap(12)
.addGroup(gl_panel_1.createParallelGroup(Alignment.LEADING)
.addComponent(lblData, GroupLayout.PREFERRED_SIZE, 29, GroupLayout.PREFERRED_SIZE)
.addComponent(lbl_DataTabeli, GroupLayout.PREFERRED_SIZE, 29, GroupLayout.PREFERRED_SIZE))
.addContainerGap(21, Short.MAX_VALUE))
);
panel_1.setLayout(gl_panel_1);
JLabel lblKod = new JLabel("Kod:");
lblKod.setFont(FONT_LABEL);
JLabel lblNazwa = new JLabel("Nazwa:");
lblNazwa.setFont(FONT_LABEL);
JLabel lblKurs = new JLabel("Kurs:");
lblKurs.setFont(FONT_LABEL);
lbl_KodWaluty = new JLabel("kod");
lbl_KodWaluty.setFont(FONT_WARTOSC);
lbl_NazwaWaluty = new JLabel("nazwa");
lbl_NazwaWaluty.setFont(FONT_WARTOSC);
lbl_KursWaluty = new JLabel("1.0000");
lbl_KursWaluty.setFont(FONT_WARTOSC);
GroupLayout gl_panel = new GroupLayout(panel);
gl_panel.setHorizontalGroup(
gl_panel.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel.createSequentialGroup()
.addContainerGap()
.addGroup(gl_panel.createParallelGroup(Alignment.LEADING, false)
.addComponent(lblKurs, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lblNazwa, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lblKod, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 95, Short.MAX_VALUE))
.addPreferredGap(ComponentPlacement.RELATED, 18, GroupLayout.PREFERRED_SIZE)
.addGroup(gl_panel.createParallelGroup(Alignment.LEADING)
.addComponent(lbl_KodWaluty, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 171, Short.MAX_VALUE)
.addComponent(lbl_NazwaWaluty, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 171, Short.MAX_VALUE)
.addComponent(lbl_KursWaluty, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 171, Short.MAX_VALUE))
.addContainerGap())
);
gl_panel.setVerticalGroup(
gl_panel.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel.createSequentialGroup()
.addContainerGap()
.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
.addComponent(lblKod)
.addComponent(lbl_KodWaluty))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
.addComponent(lblNazwa)
.addComponent(lbl_NazwaWaluty))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
.addComponent(lblKurs)
.addComponent(lbl_KursWaluty))
.addContainerGap(61, Short.MAX_VALUE))
);
panel.setLayout(gl_panel);
frmPrzelicznikWalut.getContentPane().setLayout(groupLayout);
}
protected void przelicz() {
try {
BigDecimal kwota = new BigDecimal(txtKwota.getText());
String kod = "" + comboBox_Waluta.getSelectedItem();
Waluta waluta = tabela.wyszukaj(kod);
BigDecimal wynik = null;
if(rdbtnWalutaNaZlote.isSelected()) {
wynik = waluta.przeliczNaZlote(kwota);
}
if(rdbtnZloteNaWalute.isSelected()) {
wynik = waluta.przeliczNaWalute(kwota);
}
lblPrawdziwyWynik.setText("" + wynik);
lblPrawdziwyWynik.setForeground(Color.BLUE);
} catch (Exception e) {
lblPrawdziwyWynik.setText("błąd");
lblPrawdziwyWynik.setForeground(Color.RED);
}
}
protected void wyswietlDaneWaluty() {
String kod = "" + comboBox_Waluta.getSelectedItem();
Waluta waluta = tabela.wyszukaj(kod);
if(waluta != null) {
lbl_KodWaluty.setText(waluta.getKod());
lbl_NazwaWaluty.setText(waluta.getNazwa());
lbl_KursWaluty.setText("" + waluta.getKurs());
if(! txtKwota.getText().isEmpty()) {
przelicz();
}
return;
}
lblPrawdziwyWynik.setText("0.00");
lblPrawdziwyWynik.setForeground(Color.GRAY);
}
private void pobierzAktualneKursy() {
// Pobieranie aktualnych kursów w tle (to znaczy w innym wątku)
// Aby wykonać operację w tle, w Swingu najlepiej użyć klasy SwingWorker
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
protected Void doInBackground() {
tabela = pobieranie.pobierzBiezaceKursy();
if(tabela == null) {
tabela = new Tabela("A", "Brak danych", LocalDate.now());
}
return null;
}
protected void done() {
// tu piszemy "co ma zrobić okno, gdy operacja jest zakończona"
// to będzie wykonane przez wątek EDT
odswiezDaneTabeli();
wyswietlDaneWaluty();
}
};
worker.execute();
}
private void pobierzKursyDlaDaty() {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
protected Void doInBackground() {
String data = txtData.getText();
Tabela nowaTabela = pobieranie.pobierzArchiwalneKursy(data);
tabela = nowaTabela;
SwingUtilities.invokeLater(() -> {
txtData.setForeground(Color.BLACK);
});
return null;
}
protected void done() {
odswiezDaneTabeli();
wyswietlDaneWaluty();
}
};
worker.execute();
}
private void odswiezDaneTabeli() {
lbl_NumerTabeli.setText(tabela.getNumer());
lbl_DataTabeli.setText("" + tabela.getData());
Object wybranaWaluta = comboBox_Waluta.getSelectedItem();
comboBox_Waluta.setModel(new DefaultComboBoxModel<>(tabela.getKodyWalut()));
comboBox_Waluta.setSelectedItem(wybranaWaluta);
}
}
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
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
fi
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi
# end of workaround
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
@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();
}
}
@XmlAccessorType(XmlAccessType.FIELD)
package sklep.model;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
<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>PC39-Wielomodulowy</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<!-- To jest "parent project" wobec konkretnych projektów, które dodamy tu jako moduły.
W tym pliku podaje się ogólne ustawienia. Można m.in. użyć pluginManagement i dependencyManagement
aby ustalić wersje wtyczek i zależności.
-->
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<profiles>
<profile>
<id>jakarta9</id>
<!-- Właściwe dla serwera WildFly 26 Preview -->
<activation><activeByDefault>true</activeByDefault></activation>
<properties>
<jstl.uri>http://java.sun.com/jsp/jstl/</jstl.uri>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.xml.ws</groupId>
<artifactId>jakarta.xml.ws-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</dependencyManagement>
</profile>
<profile>
<id>jakarta10</id>
<!-- Właściwe dla serwera WildFly 27+ -->
<properties>
<jstl.uri>jakarta.tags.</jstl.uri>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>10.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.xml.ws</groupId>
<artifactId>jakarta.xml.ws-api</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</profile>
</profiles>
<build>
<pluginManagement>
<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>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<modules>
<module>model</module>
<module>baza</module>
<module>rest_serwer</module>
<module>rest_klient</module>
<module>soap_api</module>
<module>soap_serwer</module>
<module>soap_klient</module>
<module>sklep_web</module>
<module>sklep_ear</module>
</modules>
</project>
\ No newline at end of file
To jest przykład "wielomodułowego projektu Maven".
Składa się on z wielu projektów-modułów, pomiędzy którymi mogą istnieć zależności.
W katalogu nadrzędnym zdefiniowany jest "parent project", który:
- jest typu pom
- sam nie zawiera żadnych źródeł
- sam nie powoduje powstawania żadnych artefaktów podczas budowania
- (teoretycznie istnieje możliwość zdefiniowania tu strony z dokumentacją src/site, ale tego nie robimy)
Plik pom w parencie:
- wymienia wszystkie moduły w elemencie <modules>
- ustala podstawowe kwestie, które są wspólne dla wszystkich modułów (np. wersja Javy)
- w elementach pluginManagement i dependencyManagement może wymienić wtyczki i biblioteki po to, aby ustalić ich wersje
! wpisanie czegość w pluginManagement lub dependencyManagement nie znaczy jeszcze, że dana rzecz będzie używana we wszystkich modułach; trzeba się do niej jawnie odwołać
Plik pom w module:
- wskazuje projekt nadrzędny w elemencie <parent>
- dziedziczy z niego m.in groupId i version, których raczej się nie zmienia (wszystkie moduły mają jednakowe)
- może mieć zależności do innych modułów tego samego projektu, przy czym:
* zabronione są zależności cykliczne
* odwołując się do modułów typu war (robimy to w ear) [generalnie, czegoś, co nie jest typu jar] trzeba dopisać <type>war</type>
* w <groupId> można wpisać ${project.groupId}, bo jest tkaie samo, jak nasze; analogicznie dla wersji
EAR
Specyfiką projektów "Enterprise Edition" jest format ear, który jest jeszcze poziom wyżej niż war: może zbierać wiele plików war i jar po to, aby wspólnie deployować je na serwer.
To dodatkowy moduł typu ear służy do budowania tego archiwum; wbrew intuicji nie robi się tego w parent.
Profile
Już po zajęciach wzbogacam ten projekt o profile Mavena i rozwiązuję w ten sposób niekompatybilność między WildFly 26 a późniejszymi wersjami.
W parent pom definiuję dwa profile "jakarta9" i "jakarta10". Domyślnym jest 9, a aby aktywować 10, należy uruchomić Mavena w wierszu poleceń
mvn -P jakarta10 clean package
lub w Eclipse / IntelliJ wskazać profil jakarta10 jako aktywny profil.
Najbardziej problematyczną rzeczą są identyfikatory uri dla taglibów w plikach JSP (w module sklep_web). Zamieniam je stosując kolejną Mavenową technikę: filtrowanie.
Plik war lub ear zbudowany zgodnie z profilem jakarta9 można wgrać na serwer WildFly 26 Preview (bo te "nie preview" obsługiwały jeszcze Java EE 8).
Plik war lub ear zbudowany zgodnie z profilem jakarta10 można wgrać na serwer WildFly 27 lub wyższy (gdy to piszę: 28).
Wgrywanie z poziomu Eclipse może nie działać.
<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>rest_klient</artifactId>
<properties>
<resteasy.version>6.2.4.Final</resteasy.version>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.json</artifactId>
<version>2.0.1</version>
</dependency>
<!-- inna implementacja: -->
<!-- <dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>jakarta.json</artifactId>
<version>1.1.2</version>
</dependency> !-->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package sklep.klient_rest;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class P01_Url {
public static void main(String[] args) {
try {
URL url = new URL(Ustawienia.ADRES_USLUGI + "/products.json");
try(InputStream inputStream = url.openStream()) {
Files.copy(inputStream, Paths.get("wynik01.json"), StandardCopyOption.REPLACE_EXISTING);
System.out.println("OK");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package sklep.klient_rest;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonReader;
import jakarta.json.JsonValue;
public class P02_Url_Json {
public static void main(String[] args) {
try {
URL url = new URL(Ustawienia.ADRES_USLUGI + "/products.json");
try(InputStream inputStream = url.openStream();
JsonReader reader = Json.createReader(inputStream)) {
JsonArray array = reader.readArray();
// System.out.println(array);
for(JsonValue jsonValue : array) {
//System.out.println(jsonValue);
System.out.println(jsonValue.asJsonObject().getString("productName"));
System.out.println(" opis: " + jsonValue.asJsonObject().getString("description", ""));
System.out.println(" cena: " + jsonValue.asJsonObject().getJsonNumber("price").bigDecimalValue());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package sklep.klient_rest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
public class P03_HttpClient {
public static void main(String[] args) {
HttpClient httpClient = HttpClient.newHttpClient();
try {
URI uri = new URI(Ustawienia.ADRES_USLUGI + "/products.json");
HttpRequest request = HttpRequest.newBuilder(uri).build();
HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
System.out.println("response " + response);
System.out.println("status: " + response.statusCode());
System.out.println("Content-Type: " + response.headers().firstValue("Content-Type").orElse("BRAK"));
String body = response.body();
System.out.println("Rozmiar treści: " + body.length());
System.out.println("\nCała treść:\n");
System.out.println(body);
} catch (URISyntaxException | IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
package sklep.klient_rest;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonObject;
import jakarta.json.JsonReader;
import jakarta.json.JsonValue;
public class P04_HttpClient_Json {
public static void main(String[] args) {
try {
JsonArray array = pobierzJsona(Ustawienia.ADRES_USLUGI + "/products.json");
for(JsonValue jsonValue : array) {
JsonObject jsonObject = jsonValue.asJsonObject();
System.out.println(jsonObject.getString("productName"));
if(jsonObject.containsKey("description")) {
System.out.println(" opis: " + jsonObject.getString("description", ""));
}
System.out.println(" cena: " + jsonObject.getJsonNumber("price").bigDecimalValue());
}
} catch (IOException | InterruptedException | URISyntaxException e) {
e.printStackTrace();
}
}
private static JsonArray pobierzJsona(String adres) throws IOException, InterruptedException, URISyntaxException {
HttpClient httpClient = HttpClient.newHttpClient();
URI uri = new URI(adres);
HttpRequest request = HttpRequest.newBuilder(uri).build();
HttpResponse<InputStream> response = httpClient.send(request, BodyHandlers.ofInputStream());
System.out.println("response " + response);
System.out.println("status: " + response.statusCode());
System.out.println("Content-Type: " + response.headers().firstValue("Content-Type").orElse("BRAK"));
return wczytajJsona(response.body());
}
private static JsonArray wczytajJsona(InputStream input) {
try(JsonReader reader = Json.createReader(input)) {
return reader.readArray();
}
}
}
package sklep.klient_rest;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Response;
// Ten i kolejne przykłady pokazują jak aplikacja kliencka napisana w Javie może wysyłać
// zapytania do usługi REST-owej (głównie GET, jest też gdzieś POST)
// korzystając z technologii JAX-RS "po stronie klienta".
// Aby z tego skorzystać, do projektu trzeba dodać bibliotekę z implementacją JAX-RS.
// Tutaj jest to resteasy-client.
public class P05_RestClient {
public static void main(String[] args) {
System.out.println("Startujemy");
Client client = ClientBuilder.newClient();
System.out.println("Przygotowuję zapytanie");
WebTarget target = client.target(Ustawienia.ADRES_USLUGI).path("products.json");
Invocation invocation = target.request().buildGet();
System.out.println("Wysyłam zapytanie");
Response response = invocation.invoke();
// Wynikiem jest obiekt klasy Response - tej samej, co na serwerze (używaliśmy np. do generowania kodów 404).
// W obiekcie można sprawdzić informacji o odpowiedzi: media type, status code.
System.out.println("Mam odpowiedź: " + response);
System.out.println("Status: " + response.getStatus());
System.out.println("C-Type: " + response.getMediaType());
System.out.println("Length: " + response.getLength());
// Aby odczytać zawartość zwróconą przez serwer, używamy metody readEntity.
// (przy domyślnych ustawieniach) tę metodę można wywołać tylko raz.
byte[] dane = response.readEntity(byte[].class);
System.out.println("Dane mają " + dane.length + " bajtów.");
try {
Files.write(Paths.get("wynik05.json"), dane);
System.out.println("Zapisałem w pliku");
} catch (IOException e) {
System.err.println(e);
}
System.out.println("Koniec");
}
}
package sklep.klient_rest;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.Response;
public class P06_RestClient_String {
public static void main(String[] args) {
Client client = ClientBuilder.newClient();
// Taki styl programowania to "fluent API"
Response response = client.target(Ustawienia.ADRES_USLUGI)
.path("products.json")
.request().buildGet()
.invoke();
System.out.println("Mam odpowiedź: " + response);
System.out.println("Status: " + response.getStatus());
System.out.println("C-Type: " + response.getMediaType());
System.out.println("Length: " + response.getLength());
// readEntity(OKREŚLENIE TYPU) stara się odczytać tresc odpowiedzi jako obiekt podanego typu
// Obsługiwane typy to m.in: byte[], String, InputStream, File
// Dodając odpowiednie "MeassgeBodyReader", możemy obsługiwać dowolne typy.
// W szczególności, gdy dodamy do projektu obsługę XML lub JSON (zob. zależności Mavena),
// będziemy mogli odczytywać dane w postaci obiektów naszego modelu, np. Product.
String dane = response.readEntity(String.class);
System.out.println("Otrzymane dane:");
System.out.println(dane);
}
}
package sklep.klient_rest;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.Response;
import sklep.model.Product;
import sklep.model.ProductList;
public class P07_RestClient_JAXB {
public static void main(String[] args) {
Client client = ClientBuilder.newClient();
Response response = client.target(Ustawienia.ADRES_USLUGI)
.path("products.xml")
.request().buildGet()
.invoke();
System.out.println("Mam odpowiedź: " + response);
System.out.println("Status: " + response.getStatus());
System.out.println("C-Type: " + response.getMediaType());
System.out.println("Length: " + response.getLength());
ProductList products = response.readEntity(ProductList.class);
System.out.println("Otrzymane dane:");
for (Product product : products.getProducts()) {
System.out.println(product);
}
}
}
package sklep.klient_rest;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.Response;
import sklep.model.Product;
import sklep.model.ProductList;
public class P08_RestClient_JSON_JedenProdukt {
public static void main(String[] args) {
Client client = ClientBuilder.newClient();
Response response = client.target(Ustawienia.ADRES_USLUGI)
.path("products.json")
.path("1")
.request().buildGet()
.invoke();
System.out.println("Mam odpowiedź: " + response);
System.out.println("Status: " + response.getStatus());
System.out.println("C-Type: " + response.getMediaType());
System.out.println("Length: " + response.getLength());
Product product = response.readEntity(Product.class);
System.out.println("Odczytany produkt: " + product);
}
}
package sklep.klient_rest;
import java.util.List;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.Response;
import sklep.model.Product;
public class P08_RestClient_JSON_Lista {
private static final GenericType<List<Product>> typListy = new GenericType<List<Product>>() {};
public static void main(String[] args) {
// Te rzeczy są "closeable", więc można tak (podobnie jak sql Connection i ResultSet)
try(Client client = ClientBuilder.newClient();
Response response = client.target(Ustawienia.ADRES_USLUGI)
.path("products.json")
.request().buildGet()
.invoke()) {
System.out.println("Mam odpowiedź: " + response);
System.out.println("Status: " + response.getStatus());
System.out.println("C-Type: " + response.getMediaType());
System.out.println("Length: " + response.getLength());
List<Product> products = response.readEntity(typListy);
for(Product product : products) {
System.out.println(product);
}
}
}
}
package sklep.klient_rest;
import java.math.BigDecimal;
import java.util.Scanner;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import sklep.model.Product;
public class P09_RestClient_Interaktywny {
public static void main(String[] args) {
System.out.println("Startujemy...");
Scanner scanner = new Scanner(System.in);
Client client = ClientBuilder.newClient();
WebTarget path = client.target(Ustawienia.ADRES_USLUGI)
.path("products")
.path("{id}");
System.out.println("Przygotowana ścieżka: " + path);
while (true) {
System.out.print("\nPodaj id: ");
int id = scanner.nextInt();
if(id == 0) break;
Response response = path
.resolveTemplate("id", id)
.request(MediaType.APPLICATION_JSON)
.get();
System.out.println("Status: " + response.getStatus());
System.out.println("Content-Type: " + response.getMediaType());
if (response.getStatus() == 200) {
Product product = response.readEntity(Product.class);
System.out.println("Mam produkt:");
System.out.println(" Nazwa: " + product.getProductName());
System.out.println(" Cena: " + product.getPrice());
System.out.println(" Opis: " + product.getDescription());
System.out.println();
System.out.println("Podaj zmianę ceny (0 aby nie zmieniać):");
BigDecimal zmianaCeny = scanner.nextBigDecimal();
if(zmianaCeny.compareTo(BigDecimal.ZERO) != 0) {
BigDecimal newPrice = product.getPrice().add(zmianaCeny);
System.out.println("PUT nowej ceny...");
Response odpPut = path.path("price").resolveTemplate("id", id).request()
.put(Entity.entity(newPrice, MediaType.TEXT_PLAIN_TYPE));
System.out.println("PUT zakończył się kodem " + odpPut.getStatus());
}
} else {
System.out.println("nie mogę odczytać");
}
}
}
}
package sklep.klient_rest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
public class P10_RestClient_PDF {
private static final MediaType PDF_TYPE = new MediaType("application", "pdf");
public static void main(String[] args) {
int productId = 1;
System.out.println("Startujemy...");
Client client = ClientBuilder.newClient();
WebTarget root = client.target(Ustawienia.ADRES_USLUGI);
Response response = root
.path("products")
.path("{id}")
.resolveTemplate("id", productId)
.request()
.accept(PDF_TYPE)
.buildGet()
.invoke();
System.out.println("Otrzymałem response: " + response);
System.out.println("Status: " + response.getStatus());
System.out.println("Content-Type: " + response.getMediaType());
if(response.getStatus() != 200) {
System.out.println("Chyba coś nie tak, więc przerywam.");
return;
}
String nazwaPliku = "wynik.pdf";
String contentDisposition = response.getHeaderString("Content-Disposition");
if(contentDisposition != null && contentDisposition.contains(";filename=")) {
nazwaPliku = contentDisposition.split(";filename=")[1];
}
try(InputStream strumienDanych = response.readEntity(InputStream.class)) {
long ileBajtow = Files.copy(strumienDanych, Paths.get(nazwaPliku), StandardCopyOption.REPLACE_EXISTING);
System.out.printf("Zapisano %d bajtów do pliku %s\n", ileBajtow, nazwaPliku);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Gotowe");
}
}
package sklep.klient_rest;
public class Ustawienia {
public static final String ADRES_USLUGI = "http://localhost:8080/rest_serwer";
}
<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>rest_serwer</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>baza</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.8</version>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
</build>
</project>
package sklep.rest;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("")
public class AplikacjaRestowa extends Application {
}
// W tej wersji cały projekt jest "jedną wielką usługą REST".
// Ale czasami może być być tak, że jest to aplikacja webowa, a tylko jej część działa na asadzie REST.
// stronka.html
// a JS z tej stronki wywołuje zaoytania RESTOWe, aby w formacie JSON pobierać sobie dane
// obsługa zapytań RESTowych może być umieszczona w podkatalogu, np. /api
// → wtedy miałoby sens napisać @ApplicationPath("/api")
package sklep.rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
// Technologia JAX-RS, część Java EE (Jakarata EE)
// Obsługa zapytań HTTP w taki sposób, aby wygodnie realizowało się usługi typu REST.
@Path("/hello")
public class Hello {
@GET
public String hello() {
return "Hello REST";
}
}
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.QueryParam;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import sklep.model.Product;
// Tak wygląda klasa wygenerowana przez polecenie "New JAX-RS Resource" w Eclipse z dodatkiem wtyczki JBossTolls (JAX-RS Tools)
// Komentuję adnotacje przed klasą, aby nie wpływało to na działanie aplikacji.
// @RequestScoped
// @Path("/products")
// @Produces({ "application/xml", "application/json" })
// @Consumes({ "application/xml", "application/json" })
public class ProductEndpoint {
@POST
public Response create(final Product product) {
//TODO: process the given product
//you may want to use the following return statement, assuming that Product#getId() or a similar method
//would provide the identifier to retrieve the created Product resource:
//return Response.created(UriBuilder.fromResource(ProductEndpoint.class).path(String.valueOf(product.getId())).build()).build();
return Response.created(null).build();
}
@GET
@Path("/{id:[0-9][0-9]*}")
public Response findById(@PathParam("id") final Long id) {
//TODO: retrieve the product
Product product = null;
if (product == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(product).build();
}
@GET
public List<Product> listAll(@QueryParam("start") final Integer startPosition,
@QueryParam("max") final Integer maxResult) {
//TODO: retrieve the products
final List<Product> products = null;
return products;
}
@PUT
@Path("/{id:[0-9][0-9]*}")
public Response update(@PathParam("id") Long id, final Product product) {
//TODO: process the given product
return Response.noContent().build();
}
@DELETE
@Path("/{id:[0-9][0-9]*}")
public Response deleteById(@PathParam("id") final Long id) {
//TODO: process the product matching by the given id
return Response.noContent().build();
}
}
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.exn.DBException;
import sklep.exn.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.OrderDAO;
import sklep.exn.DBException;
import sklep.exn.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();
}
}
}
package sklep.rest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
import sklep.model.ProductList;
@Path("/products.pdf")
@Produces("application/pdf")
public class RProductPDF {
@GET
public ProductList readAllProducts() throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return new ProductList(productDAO.readAll());
}
}
@Path("/{id}")
@GET
public Product readOneProduct(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findById(productId);
}
}
}
package sklep.rest;
import java.math.BigDecimal;
import java.net.URI;
import java.util.List;
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.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
import sklep.model.ProductList;
import sklep.photo.PhotoUtil;
/* JAX-RS - część Javy EE
* Aplikacja (projekt) składa się z wielu "resource classes", z których każda obsługuje pewien rodzaj encji
* np. pod adresem /products działa ta klasa RProduct, która zajmuje się obsługą produktów
* a pod adresem /orders działa ROrder, która zajmuje się zamówieniami.
* Przykładowa dobra (szczegółowa) dokumentacja: https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest3x/index.html
*
* W usługach REST struktura adresów URL odpowiada logicznej strukturze danych.
* Typowe jest, że * pod adresem "katalogu", np. /products , dostępna jest lista wszystkich rekordów
* (lub ogólne informacje, lub jakiś sposób przeglądania - gdyby było ich za dużo do zwrócenia na raz)
* a idąc dalej wgłąb ścieżki przechodzimy do "pojedynczych rekordów", np. /products/2
*
* W tej klasie wejdziemy nawet do wnętrza produktów i udostępnimy osobno cenę
* /products/2/price
* W praktyce nie robi się tego zbyt często, ale tu zobaczymy jako przykład możliwości.
*
* To od programisty (twórcy usługi) zależy jakie adresy i jakie operacje pod tymi adresami udostępnia.
*
* Ta klasa udostępnia (i przyjmuje) dane produktów w różnych formatach.
* Gdy w Produces jest wiele formatów, to klient może wybrać za pomocą nagłówka Accept
* Gdy w Consumes jest wiele formatów, to klient może przysłać dane w dowolnym z nich (nagłówek Content-Type)
*
*/
@Path("/products")
public class RProducts {
@GET
@Produces({"application/json", "application/xml", "text/plain", "application/pdf"})
public ProductList wszystkieProdukty() throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return new ProductList(productDAO.readAll());
}
}
// Może też być tak, że kilka metod działa pod tym samym adresem, ale służą one do tworzenia odpowiedzi w różnych formatach.
// Przykład: tworzenie HTML w oddzielnej metodzie
@GET
@Produces("text/html;charset=UTF-8")
public String wszystkieProduktyHTML() throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
List<Product> products = productDAO.readAll();
StringBuilder txt = new StringBuilder("<!DOCTYPE html>\n<html><body>\n");
txt.append("<h1>Lista produktów</h1>\n");
for(Product product : products) {
txt.append(product.toHtml()).append('\n');
}
txt.append("</body></html>");
return txt.toString();
}
}
@GET
@Path("/{id}")
@Produces({"application/json", "application/xml", "text/plain", "application/pdf"})
public Product jedenProdukt(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findById(productId);
}
}
@Path("/{id}")
@GET
@Produces("text/html;charset=UTF-8")
public String jedenProduktHTML(@PathParam("id") int productId) throws DBException, RecordNotFound {
Product product = jedenProdukt(productId);
return "<!DOCTYPE html>\n<html><body>" + product.toHtml() + "</body></html>";
}
@GET
@Path("/{id}/price")
@Produces({"application/json", "text/plain"})
public BigDecimal getPrice(@PathParam("id") int productId) throws DBException, RecordNotFound {
return jedenProdukt(productId).getPrice();
}
// W zapytaniach typu PUT i POST klient wysyła dane na serwer (treść zapytania: "body" / "entity" / "content" ...)
// W JAX-RS metoda może posiadać tylko jeden parametr bez adnotacji i to właśnie przez ten parametr przekazywana jest treść zapytania
// (te dane, które przysłał klient). Format tych danych opisuje adnotacja @Consumes
@PUT
@Path("/{id}/price")
@Consumes({"application/json", "text/plain"})
public void setPrice(@PathParam("id") int productId, BigDecimal newPrice) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
Product product = productDAO.findById(productId);
product.setPrice(newPrice);
productDAO.save(product);
db.commit();
}
}
// W praktyce REST metoda POST jest używana do dodawania nowych rekordów do katalogu.
// Może być także używana w innych celach - gdy klient ma "przysłać dane na serwer", a serwer coś z tym zrobi (podobnie jak to było w SOAP).
@POST
@Consumes({"application/json", "application/xml"})
@Produces({"application/json", "application/xml"})
public Response zapiszProdukt(Product product) throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.save(product);
db.commit();
URI uri = UriBuilder.fromResource(RProducts.class).path("/{id}").build(product.getProductId());
return Response.created(uri).build();
}
}
// PUT vs POST
// PUT powinniśmy używać wtedy, gdy z góry wiadomo, pod jakim adresem zostaną zapisane dane.
// W praktyce PUT używa się najczęściej do aktualizacji istniejących danych, ale teoretycznie PUT
// można też użyć do zapisania nowych danych pod konkretnym adresem.
// Gdy wyślemy dane za pomocą PUT pod adres, to następnie GET z tego samego adresu powinien odczytać dane, które wysłał PUT (być może w innym formacie).
// POST używajmy wtedy, gdy nie wiemy z góry pod jakim adresem dane zostaną zapisane, np. gdy id rekordu jest generowane z sekwencji.
// Taka metoda powinna dać w odpowiedzi informację o ID utworzonego rekordu.
// Kilka możliwości:
// 1) (w tej klasie) - odesłanie uzupełnionego rekordu - trochę niewydajne, ale wygodne
// 2) minimalny dokumencik JSON, w którego wnętrzu jest zawarta ta informacja (tak robi wiele usług w praktyce, np. PayU)
// 3) (zobaczymy jeszcze) - odesłać odpowiedź typu Created z nagłówkiem Location - najlepsze z punktu widzenia standardów/dobrych praktyk
@DELETE
@Path("/{id}")
public void usun(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.delete(productId);
db.commit();
}
}
@GET
@Path("/{id}/photo")
@Produces("image/jpeg")
public byte[] foto(@PathParam("id") int productId) throws DBException, RecordNotFound {
return PhotoUtil.readBytes(productId);
}
}
package sklep.rest;
import java.math.BigDecimal;
import java.util.List;
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 sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
import sklep.photo.PhotoUtil;
@Path("/products.json")
@Consumes("application/json")
@Produces("application/json")
public class RProductsJSON {
// Wszystkie metody, które coś produkują (czyli nie są void), produkują JSON-a
// (wyjątkiem jest photo - na pozimie metody można to ustawienie zmienić)
// Wszystkie metody, które coś konsumują (czyli posiadają parametr, w którym odbierane jest "ciało zapytania")
// konsumują JSON.
@GET
public List<Product> wszystkieProdukty() throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.readAll();
}
}
@GET
@Path("/{id}")
public Product produktWgId(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findById(productId);
}
}
@GET
@Path("/{id}/price")
public BigDecimal getPrice(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findById(productId).getPrice();
}
}
@PUT
@Path("/{id}/price")
public void setPrice(@PathParam("id") int productId, BigDecimal newPrice) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
Product product = productDAO.findById(productId);
product.setPrice(newPrice);
productDAO.save(product);
db.commit();
}
}
@POST
public Product zapiszProdukt(Product product) throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.save(product);
db.commit();
return product;
}
}
@DELETE
@Path("/{id}")
public void usun(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.delete(productId);
db.commit();
}
}
@GET
@Path("/{id}/photo")
@Produces("image/jpeg")
public byte[] foto(@PathParam("id") int productId) throws DBException, RecordNotFound {
return PhotoUtil.readBytes(productId);
}
}
package sklep.rest;
import java.math.BigDecimal;
import java.util.List;
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 sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
import sklep.model.ProductList;
import sklep.photo.PhotoUtil;
@Path("/products.xml")
public class RProductsXML {
@GET
@Produces("application/xml")
public ProductList wszystkieProdukty() throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return new ProductList(productDAO.readAll());
}
}
@GET
@Path("/{id}")
@Produces("application/xml")
public Product produktWgId(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findById(productId);
}
}
// Pojedyncza wartość liczbowa lub tekstowa NIE JEST poprawnym (samodzielnym) XML-em
// Dlatego tu wyjątkowo użjemy text/plain.
@GET
@Path("/{id}/price")
@Produces("text/plain")
public BigDecimal getPrice(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findById(productId).getPrice();
}
}
@PUT
@Path("/{id}/price")
@Consumes("text/plain")
public void setPrice(@PathParam("id") int productId, BigDecimal newPrice) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
Product product = productDAO.findById(productId);
product.setPrice(newPrice);
productDAO.save(product);
db.commit();
}
}
@POST
@Consumes("application/xml")
@Produces("application/xml")
public Product zapiszProdukt(Product product) throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.save(product);
db.commit();
return product;
}
}
@DELETE
@Path("/{id}")
public void usun(@PathParam("id") int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.delete(productId);
db.commit();
}
}
@GET
@Path("/{id}/photo")
@Produces("image/jpeg")
public byte[] foto(@PathParam("id") int productId) throws DBException, RecordNotFound {
return PhotoUtil.readBytes(productId);
}
}
package sklep.rest.ext;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import jakarta.servlet.ServletContext;
import jakarta.ws.rs.WebApplicationException;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.util.JAXBSource;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.xmlgraphics.util.MimeConstants;
public class ObslugaXSL {
private ServletContext servletContext;
public ObslugaXSL(ServletContext servletContext) {
this.servletContext = servletContext;
}
// obiekt -(za pomocą JAXB)-> XML -(za pomocą transformera)-> XML(XSL-FO)
// --(za pomocą Apache FOP)-> PDF -> output
public void wypiszPDF(Object obj, OutputStream output) {
try(InputStream configStream = servletContext.getResourceAsStream("WEB-INF/fop-conf.xml")) {
JAXBContext ctx = JAXBContext.newInstance(obj.getClass());
JAXBSource src = new JAXBSource(ctx, obj);
FopFactory fopFactory = FopFactory.newInstance(new URI(""), configStream);
try(BufferedOutputStream pdfOut = new BufferedOutputStream(output)) {
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, pdfOut);
TransformerFactory tf = TransformerFactory.newInstance();
StreamSource xsl = new StreamSource(servletContext.getResourceAsStream("WEB-INF/sklep-fo.xsl"));
Transformer tr = tf.newTransformer(xsl);
Result res = new SAXResult(fop.getDefaultHandler());
tr.transform(src, res);
}
} catch (Exception e) {
e.printStackTrace();
throw new WebApplicationException("Problem FOP " + e.getMessage(), e, 500);
}
}
}
package sklep.rest.ext;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import jakarta.servlet.ServletContext;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.MessageBodyWriter;
import jakarta.ws.rs.ext.Provider;
import sklep.model.Product;
import sklep.model.ProductList;
@Provider
public class PDFWriter implements MessageBodyWriter<Object> {
private static final MediaType PDF_TYPE = new MediaType("application", "pdf");
@Context
private ServletContext servletContext;
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return (type == ProductList.class || type == Product.class) && PDF_TYPE.isCompatible(mediaType);
}
@Override
public void writeTo(Object obj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream output)
throws IOException, WebApplicationException {
String fileName = "products.pdf";
if (obj instanceof Product) {
Product product = (Product) obj;
fileName = product.getProductName().replace(' ', '_') + ".pdf";
}
// httpHeaders.add("Content-Disposition", "attachment;filename=" + fileName);
httpHeaders.add("Content-Disposition", "inline;filename=" + fileName);
ObslugaXSL obslugaXSL = new ObslugaXSL(servletContext);
obslugaXSL.wypiszPDF(obj, output);
}
}
package sklep.rest.ext;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import sklep.exn.RecordNotFound;
@Provider
public class RecordNotFoundMapper implements ExceptionMapper<RecordNotFound> {
@Override
public Response toResponse(RecordNotFound exception) {
String html = "<html><body><h1>Nie znaleziono</h1><p style='color:red'>"
+ exception.getMessage() + "</p></body></html>";
return Response.status(404)
.type("text/html;charset=utf-8")
.entity(html)
.build();
}
}
package sklep.rest.ext;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import sklep.exn.SklepException;
@Provider
public class SklepExceptionMapper implements ExceptionMapper<SklepException> {
@Override
public Response toResponse(SklepException e) {
String tresc = String.format("<html><body>"
+ "<h1>Błąd</h1>"
+ "<div>Błąd typu <code>%s</code></div>"
+ "<div style='color:red'>%s</div>"
+ "</body></html>",
e.getClass().getName(),
e.getMessage());
return Response.status(Status.INTERNAL_SERVER_ERROR)
.type("text/html;charset=utf-8")
.entity(tresc)
.build();
}
}
// a gdybyśmy chcieli mapować wszystkie wyjątki
// public class DefaultExceptionMapper implements ExceptionMapper<Exception> { ... }
<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- $Id: fop.xconf 447325 2006-09-18 08:55:33Z jeremias $ -->
<!--
This is an example configuration file for FOP.
This file contains the same settings as the default values
and will have no effect if used unchanged.
Relative config url's will be resolved relative to
the location of this file.
-->
<!-- NOTE: This is the version of the configuration -->
<fop version="1.0">
<!-- Base URL for resolving relative URLs -->
<base>.</base>
<!-- Source resolution in dpi (dots/pixels per inch) for determining the size of pixels in SVG and bitmap images, default: 72dpi -->
<source-resolution>72</source-resolution>
<!-- Target resolution in dpi (dots/pixels per inch) for specifying the target resolution for generated bitmaps, default: 72dpi -->
<target-resolution>72</target-resolution>
<!-- Default page-height and page-width, in case
value is specified as auto -->
<default-page-settings height="11in" width="8.26in"/>
<!-- Information for specific renderers -->
<!-- Uses renderer mime type for renderers -->
<renderers>
<renderer mime="application/pdf">
<filterList>
<!-- provides compression using zlib flate (default is on) -->
<value>flate</value>
<!-- encodes binary data into printable ascii characters (default off)
This provides about a 4:5 expansion of data size -->
<!-- <value>ascii-85</value> -->
<!-- encodes binary data with hex representation (default off)
This filter is not recommended as it doubles the data size -->
<!-- <value>ascii-hex</value> -->
</filterList>
<fonts>
<!-- embedded fonts -->
<!--
This information must exactly match the font specified
in the fo file. Otherwise it will use a default font.
For example,
<fo:inline font-family="Arial" font-weight="bold" font-style="normal">
Arial-normal-normal font
</fo:inline>
for the font triplet specified by:
<font-triplet name="Arial" style="normal" weight="bold"/>
If you do not want to embed the font in the pdf document
then do not include the "embed-url" attribute.
The font will be needed where the document is viewed
for it to be displayed properly.
possible styles: normal | italic | oblique | backslant
possible weights: normal | bold | 100 | 200 | 300 | 400
| 500 | 600 | 700 | 800 | 900
(normal = 400, bold = 700)
-->
<!--
<font kerning="yes" embed-url="/usr/share/fonts/truetype/msttcorefonts/arial.ttf">
<font-triplet name="Arial" style="normal" weight="normal"/>
<font-triplet name="ArialMT" style="normal" weight="normal"/>
</font>
<font kerning="yes" embed-url="/usr/share/fonts/truetype/msttcorefonts/arial.ttf">
<font-triplet name="Arial" style="normal" weight="normal"/>
<font-triplet name="ArialMT" style="normal" weight="normal"/>
</font>
-->
<!-- PC -->
<directory recursive="true">/usr/share/fonts/truetype/</directory>
<directory recursive="true">C:\Windows\Fonts</directory>
</fonts>
<!-- This option lets you specify additional options on an XML handler -->
<!--xml-handler namespace="http://www.w3.org/2000/svg">
<stroke-text>false</stroke-text>
</xml-handler-->
</renderer>
<renderer mime="application/postscript">
<!-- This option forces the PS renderer to rotate landscape pages -->
<!--auto-rotate-landscape>true</auto-rotate-landscape-->
<!-- This option lets you specify additional options on an XML handler -->
<!--xml-handler namespace="http://www.w3.org/2000/svg">
<stroke-text>false</stroke-text>
</xml-handler-->
</renderer>
<renderer mime="application/vnd.hp-PCL">
</renderer>
<!-- MIF does not have a renderer
<renderer mime="application/vnd.mif">
</renderer>
-->
<renderer mime="image/svg+xml">
<format type="paginated"/>
<link value="true"/>
<strokeText value="false"/>
</renderer>
<renderer mime="application/awt">
</renderer>
<renderer mime="image/png">
<!--transparent-page-background>true</transparent-page-background-->
</renderer>
<renderer mime="image/tiff">
<!--transparent-page-background>true</transparent-page-background-->
<!--compression>CCITT T.6</compression-->
</renderer>
<renderer mime="text/xml">
</renderer>
<!-- RTF does not have a renderer
<renderer mime="text/rtf">
</renderer>
-->
<renderer mime="text/plain">
<pageSize columns="80"/>
</renderer>
</renderers>
</fop>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" encoding="utf-8" />
<xsl:template match="/">
<fo:root font-family="Arial">
<fo:layout-master-set>
<fo:simple-page-master master-name="A4"
page-width="210mm" page-height="297mm" margin-top="1cm"
margin-bottom="1cm" margin-left="1.5cm" margin-right="1.5cm">
<fo:region-body margin="2cm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4">
<fo:flow flow-name="xsl-region-body">
<xsl:apply-templates />
<fo:block/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="product">
<fo:block-container space-before.minimum="0.5em"
page-break-inside="avoid">
<fo:block>
<xsl:text>Produkt nr </xsl:text>
<xsl:value-of select="@id" />
<xsl:text>.</xsl:text>
</fo:block>
<fo:block-container margin="1em"
border-style="solid" border-width="2.5pt" padding="3pt"
border-color="#2233AA">
<fo:block font-weight="bold" font-size="14pt"
margin-bottom="1em" color="#FF2244">
<xsl:apply-templates select="product-name" />
</fo:block>
<fo:block font-weight="bold" color="green">
<xsl:text>Cena: </xsl:text>
<xsl:value-of select="price" />
</fo:block>
<fo:block color="green">
<xsl:text>VAT: </xsl:text>
<fo:inline font-style="italic">
<xsl:value-of select="vat * 100" />
<xsl:text>%</xsl:text>
</fo:inline>
</fo:block>
<fo:block font-size="12pt" margin-bottom="1em">
<xsl:apply-templates select="description" />
</fo:block>
</fo:block-container>
</fo:block-container>
</xsl:template>
</xsl:stylesheet>
<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>sklep_ear</artifactId>
<packaging>ear</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sklep_web</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rest_serwer</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>soap_serwer</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
</dependencies>
</project>
\ No newline at end of file
<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>sklep_web</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>baza</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>src/main/webapp</directory>
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package sklep.basket;
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;
import jakarta.servlet.http.HttpSession;
import sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.model.Product;
@WebServlet("/add_to_basket")
public class AddToBasket extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
int productId = Integer.parseInt(request.getParameter("productId"));
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
Product product = productDAO.findById(productId);
HttpSession sesja = request.getSession();
Basket basket = (Basket)sesja.getAttribute("basket");
basket.addProduct(product);
}
} catch(Exception e) {
// ignorujemy błędy
}
// Przekierowanie - każemy przeglądarce wejść pod ten adres.
response.sendRedirect("products9.jsp");
}
}
package sklep.basket;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import sklep.model.Product;
public class Basket {
private final Map<Integer, ProductInBasket> elementy = new HashMap<>();
public synchronized void addProduct(Product product, int quantity) {
if(elementy.containsKey(product.getProductId())) {
// jeśli w słowniku jest już taki element, to tylko zwiększamy ilość
elementy.get(product.getProductId()).increaseQuantity(quantity);
} else {
// jeśli jeszcze nie ma, to tworzymy
elementy.put(product.getProductId(),
new ProductInBasket(product.getProductId(), product.getProductName(), product.getPrice(), quantity));
}
}
public synchronized void addProduct(Product product) {
// "domyślną ilością, o którą zwiększamy, jest 1"
addProduct(product, 1);
}
public synchronized Collection<ProductInBasket> getElements() {
return Collections.unmodifiableCollection(elementy.values());
}
public synchronized BigDecimal getTotalValue() {
return getElements().stream()
.map(ProductInBasket::getValue)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
@Override
public synchronized String toString() {
return "Koszyk o rozmiarze " + getElements().size()
+ " i wartości " + getTotalValue();
}
}
package sklep.basket;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
@WebListener
public class BasketInitialization implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
session.setMaxInactiveInterval(30); // sesja wygasa po pół minuty - tylko na potrzeby zajęć
session.setAttribute("basket", new Basket());
System.out.println("Początek sesji " + session.getId());
}
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("Koniec sesji " + se.getSession().getId());
}
}
package sklep.basket;
import java.math.BigDecimal;
import java.util.Objects;
public class ProductInBasket {
private int productId;
private String productName;
private BigDecimal price;
private int quantity;
public ProductInBasket(int productId, String productName, BigDecimal price, int quantity) {
this.productId = productId;
this.productName = productName;
this.price = price;
this.quantity = quantity;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int getProductId() {
return productId;
}
public String getProductName() {
return productName;
}
@Override
public String toString() {
return "ElementKoszyka [productId=" + productId + ", productName=" + productName + ", price=" + price
+ ", quantity=" + quantity + "]";
}
@Override
public int hashCode() {
return Objects.hash(price, productId, productName, quantity);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ProductInBasket other = (ProductInBasket) obj;
return Objects.equals(price, other.price) && productId == other.productId
&& Objects.equals(productName, other.productName) && quantity == other.quantity;
}
public BigDecimal getValue() {
return price.multiply(BigDecimal.valueOf(quantity));
}
public void increaseQuantity(int change) {
quantity += change;
}
}
package sklep.photo;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
// Włączona obsługa zapytań multipart ("z załącznikami"). Maks rozmiar zapytania/pliku: 16M
@WebServlet("/doUploadPhoto")
@MultipartConfig(maxRequestSize = 16 * 1024 * 1024)
public class DoUploadPhoto extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String paramId = request.getParameter("productId");
if(paramId != null) {
int productId = Integer.parseInt(paramId);
Part part = request.getPart("plik");
if(part != null) {
// przysłano plik
// Tutaj nazwa pliku jest dla nas bez znaczenia, ale gdybyśmy potrzebowali, to w ten sposób:
// String nazwaPliku = part.getSubmittedFileName();
// Przypisujemy bajty ze strumienia do pliku w katalogu ze zdjęciami:
PhotoUtil.writeStream(productId, part.getInputStream());
}
}
} catch (Exception e) {
// wypisujemy błąd, ale metoda kończy się normalnie
e.printStackTrace();
}
response.sendRedirect("products9.jsp");
}
}
package sklep.photo;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import sklep.exn.RecordNotFound;
@WebServlet("/photo")
public class Photo extends HttpServlet {
private static final long serialVersionUID = 1L;
/* Ten serwlet wczytuje z dysku plik ze zdjęciem o podanym numerze (z parametru productId).
* Aby odesłać odpowiedź "binarną" (a nie tekstową) używamy getOutputStream() zamiast getWriter().
* Aby przeglądarka wiedziała, że otrzymuje grafikę, ustawiamy content-type image/jpeg.
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String parametrId = request.getParameter("productId");
if(parametrId == null) {
return;
}
try {
int id = Integer.parseInt(parametrId);
byte[] bytes = PhotoUtil.readBytes(id);
response.setContentType("image/jpeg");
ServletOutputStream output = response.getOutputStream();
output.write(bytes);
output.close();
} catch (RecordNotFound e) {
response.setStatus(404);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().println("Nie ma zdjęcia dla produktu o nr " + parametrId);
} catch (Exception e) {
response.setStatus(500);
e.printStackTrace();
}
}
}
package sklep.web;
import java.io.IOException;
import java.math.BigDecimal;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
@WebServlet("/edit_product")
public class EditProduct extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String parametrId = request.getParameter("productId");
if(parametrId != null) {
int productId = Integer.parseInt(parametrId);
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
Product product = productDAO.findById(productId);
// Gdy do obiektu request dodamy atrybut, to stanie się on dostępny dla kolejnych komponentów
// naszej aplikacji, które będą obsługiwać to zapytanie.
// W tym przypadku skrypt JSP może odwoływać się do obiektu product.
// Obiekt request jest też nośnikiem danych, podobnie jak sesja i servletContext.
// To działa jak Model w Spring MVC.
// Tylko jeśli znajdę produkt, tylko wtedy dodaję go do requestu i JSP wyświetli jego dane.
// Jeśli parametru productId nie było lub produktu nie znaleziono, to wyświetli się pusty formularz.
request.setAttribute("product", product);
} catch (DBException | RecordNotFound e) {
e.printStackTrace();
}
}
// Forward to "wewnętrzne przekierowanie" obsługi zapytania do innego komponentu aplikacji.
// Tutaj "wyświetlamy" formularz edycji produktu.
RequestDispatcher dispatcher = request.getRequestDispatcher("product_form.jsp");
if(dispatcher != null)
dispatcher.forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// W tej wersji nie obsługujemy błędów - w razie błędu wyświetli się strona z wyjątkiem
// W przypadku braku ID zostanie utworzony nowy produkt, a w przypadku podania ID (gdy to była edycja istniejącego) - zostanie zastąpiony.
request.setCharacterEncoding("UTF-8");
String parametrId = request.getParameter("productId");
Integer productId = (parametrId == null || parametrId.isEmpty()) ? null : Integer.valueOf(parametrId);
String parametrPrice = request.getParameter("price");
BigDecimal price = new BigDecimal(parametrPrice);
String parametrVat = request.getParameter("vat");
BigDecimal vat = (parametrVat == null || parametrVat.isEmpty()) ? null : new BigDecimal(parametrVat);
String name = request.getParameter("productName");
String description = request.getParameter("description");
Product product = new Product(productId, name, price, vat, description);
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.save(product);
db.commit();
// Gdy udało się zapisać, to przejdziemy z powrotem do listy.
response.sendRedirect("products9.jsp");
} catch (DBException e) {
e.printStackTrace();
}
}
}
package sklep.web;
import java.math.BigDecimal;
import java.util.List;
import sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.model.Product;
public class ProductBean {
private BigDecimal minPrice, maxPrice;
// Chociaż wewnętrznie zmienna jest typu BigDecimal, to gettery i settery napiszemy tak, jakby to były Stringi.
// Robimy to po to, aby w JSP zadziałało setProperty.
public String getMinPrice() {
return minPrice == null ? null : minPrice.toString();
}
public void setMinPrice(String minPrice) {
if(minPrice == null || minPrice.isEmpty()) {
this.minPrice = null;
} else {
this.minPrice = new BigDecimal(minPrice);
}
}
public String getMaxPrice() {
return maxPrice == null ? null : maxPrice.toString();
}
public void setMaxPrice(String maxPrice) {
if(maxPrice == null || maxPrice.isEmpty()) {
this.maxPrice = null;
} else {
this.maxPrice = new BigDecimal(maxPrice);
}
}
// Metoda odczytuje produkty zgodnie z ustawionymi wcześniej kryteriami (w tym przykładzie są to ceny, ale może być więcej filtrów).
public List<Product> getFilteredProducts() throws DBException {
try(DBConnection db = DBConnection.open()) {
return db.productDAO().findByPrice(minPrice, maxPrice);
}
}
// Metoda wygląda jak getter, ale wewnętrznie czyta dane z bazy, a nie z własnej zmiennej.
public List<Product> getAllProducts() throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.readAll();
}
}
}
package sklep.web;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
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("/products0")
public class Products0 extends HttpServlet {
private static final long serialVersionUID = 1L;
/* W tej wersji serwlet wypisuje dane produktów tekstowo.
* W kodzie serwletu bezpośrednio korzytstamy z technologii bazodanowej JDBC,
* mamy tutaj fragment SQL, nazwy tabel, kolumn, odczyt pojedynczych pól...
*
* Na dłuższą metę tak się nie programuje: byłoby za dużo kodu, mieszalibyśmy wartwę webową z warstwą danych,
* mielibyśmy dużo duplikacji kodu pomiędzy różnymi klasami w aplikacji.
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
try(Connection c = DriverManager.getConnection("jdbc:postgresql://localhost/sklep", "kurs", "abc123");
Statement stmt = c.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM products")) {
while(rs.next()) {
out.printf("Produkt nr %d: %s za cenę %s.",
rs.getInt("product_id"), rs.getString("product_name"), rs.getBigDecimal("price"));
String opis = rs.getString("description");
if(opis != null) {
out.printf(" Opis: %s.", opis);
}
out.println();
}
} catch (SQLException e) {
e.printStackTrace(out);
}
}
}
package sklep.web;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.model.Product;
@WebServlet("/products1")
public class Products1 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
List<Product> products = productDAO.readAll();
out.println("Odczytano " + products.size() + " produktów:");
for(Product product : products) {
out.println(product);
}
} catch (DBException e) {
e.printStackTrace(out);
}
}
}
package sklep.web;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import sklep.db.DBConnection;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.model.Product;
@WebServlet("/products2")
public class Products2 extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Lista produktów</title>");
out.println("<link rel='stylesheet' type='text/css' href='styl.css'>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Lista produktów</h1>");
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
List<Product> products = productDAO.readAll();
out.printf("<div>Odczytano %d produktów</div>", products.size());
for(Product product : products) {
out.println(product.toHtml());
}
} catch (DBException e) {
out.println("<pre class='error'>");
e.printStackTrace(out);
out.println("</pre>");
}
out.println("</body>");
out.println("</html>");
}
}
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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sklep - spis treści</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Sklep Web</h1>
<h2>Lista produktów w różnych wersjach</h2>
<h3>Wersje niedoskonałe, nie do naśladowania.</h3>
<ul>
<li><a href="products0">wersja 0</a> - samodzielny prosty serwlet</li>
<li><a href="products1">wersja 1</a> - serwlet oparty o klasy DAO</li>
<li><a href="products2">wersja 2</a> - serwlet oparty o klasy DAO z HTMLem</li>
<li><a href="products3.jsp">wersja 3</a> - JSP ze skryptletami</li>
<li><a href="products4.jsp">wersja 4</a> - JSP z tagami SQL</li>
</ul>
<h3>Wersje przyzwoite ;-)</h3>
<ul>
<li><a href="products5.jsp">wersja 5</a> - JSP oparty o klasę bean</li>
<li><a href="products6.jsp">wersja 6</a> - wyszukiwanie wg ceny - bean</li>
<li><a href="products7.jsp">wersja 7</a> - fotki</li>
<li><a href="products8.jsp">wersja 8</a> - koszyk</li>
<li><a href="products9.jsp">wersja 9</a> - edycja produktów</li>
</ul>
<h3>Dodatkowe strony</h3>
<ul>
<li><a href="photo?productId=2">Foto</a> - przykładowe zdjęcie</li>
<li><a href="upload_photo.jsp?productId=2">Zmień zdjęcie</a> nr 2</li>
<li><a href="edit_product?productId=1">Edytuj produkt nr 1</a></li>
<li><a href="edit_product">Dodaj nowy produkt</a></li>
</ul>
</body>
</html>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="${jstl.uri}core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Edycja danych produktu</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Edycja produktu</h1>
<form id="product-form" method="post">
<table class="form">
<tr>
<td><label for="productId">Numer:</label></td>
<td><input name="productId" placeholder="brak" type="number" readonly="readonly" value="${product.productId}"/></td>
</tr>
<tr>
<td><label for="productName">Nazwa towaru:</label></td>
<td><input name="productName" placeholder="nazwa..." type="text" value="${product.productName}"/>
</td>
</tr>
<tr>
<td><label for="price">Cena:</label></td>
<td><input name="price" placeholder="12.90" title="tu wpisz cenę" type="number" step="0.01" value="${product.price}"/>
</td>
</tr>
<tr>
<td><label for="vat">Stawka VAT:</label></td>
<td><input name="vat" placeholder="0.23" title="tu wpisz vat" type="number" step="0.01" value="${product.vat}"/>
</td>
</tr>
<tr>
<td><label for="description">Opis:</label></td>
<td><textarea name="description" rows="10" cols="120">${product.description}</textarea></td>
</tr>
<tr>
<td><button>Zapisz</button></td>
</tr>
</table>
</form>
<div class="action"><a href="products9.jsp">powrót do listy produktów</a></div>
<div class="action"><a href="index.html">powrót do spisu treści</a></div>
</body>
</html>
<%@page import="sklep.model.Product"%>
<%@page import="java.util.List"%>
<%@page import="sklep.db.ProductDAO"%>
<%@page import="sklep.db.DBConnection"%>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista produktów 3</title>
</head>
<body>
<h1>Lista produktów - wersja 3 JSP</h1>
<p>W tej wersji wewnątrz skryptu JSP umieścimy fragmenty Javy, tzw. scriptlets.
To jest pierwsza techniczna możliwość, którą oferował standard JSP.</p>
<p>Uwaga - ta wersja nadal jest nieporządna i <strong>nie jest</strong> wzorem do naśladowania.
Mieszanie kodu HTML z Javą w taki sposób, szczególnie jak zrobiliśmy z pętlą pod koniec, jest w bardzo złym stylu.
</p>
<p>[<a href="index.html">powrót do spisu treści</a>]</p>
<%-- Poniżej skryptlet, który otwiera połączenie z bazą danych: --%>
<%
DBConnection db = DBConnection.open();
ProductDAO dao = db.productDAO();
%>
<p>Pobieranie danych...</p>
<%
List<Product> products = dao.readAll();
%>
<p>Pobrano <%= products.size() %> produktów. </p>
<h3>Wszystkie produkty</h3>
<ul>
<% for(Product product : products) { %>
<li><%= product.getProductName() %> za <%= product.getPrice() %></li>
<% } %>
</ul>
<% db.close(); %>
<p>[<a href="index.html">powrót do spisu treści</a>]</p>
</body>
</html>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="${jstl.uri}core"%>
<%@taglib prefix="sql" uri="${jstl.uri}sql"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista produktów 4</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Lista produktów - wersja 4 JSP</h1>
<p>W tej wersji korzystamy ze standardowej biblioteki tagów (JSTL), a dokładnie z jej fragmentu obsługującego SQL.</p>
<p>Bezpośrednio w JSP wykonamy zapytanie w bazie danych - to też jeszcze nie będzie najładniejszy styl...</p>
<%-- W tej wersji za pomocą dedykowanych tagów JSP zadamy zapytanie SQL.
Ta wersja W OGÓLE nie używa klas stworzonych przez nas w projekcie; jest samowystarczalna.
--%>
<%-- "taglibs" - biblioteki tagów, zaimplementowane w Javie, a w JSP używa się ich za pomocą składni "tagowej" (dokładnie składni XML) --%>
<sql:setDataSource var="baza" driver="org.postgresql.Driver"
url="jdbc:postgresql://localhost/sklep"
user="kurs" password="abc123"/>
<sql:query dataSource="${baza}" scope="page" var="result">
SELECT * FROM products ORDER BY product_id
</sql:query>
<%-- .product_name .price itp - to są nazwy kolumn w tabeli SQL --%>
<c:forEach var="row" items="${result.rows}">
<div class="product">
<h3>${row.product_name}</h3>
<div class="price">${row.price}</div>
<p>${row.description}</p>
</div>
</c:forEach>
</body>
</html>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="${jstl.uri}core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista produktów 5</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Lista produktów - wersja 5 JSP</h1>
<jsp:useBean id="productBean" class="sklep.web.ProductBean"/>
<c:forEach var="p" items="${productBean.allProducts}">
<div class="product">
<h3>${p.productName}</h3>
<div class="price">Cena: ${p.price}</div>
<div class="price">VAT ${p.vat * 100}%</div>
<c:if test="${not empty p.description}">
<p>${p.description}</p>
</c:if>
</div>
</c:forEach>
</body>
</html>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="${jstl.uri}core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista produktów 6</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Lista produktów - wersja 6 JSP z filtrem</h1>
<jsp:useBean id="productBean" class="sklep.web.ProductBean"/>
<form id="wyszukiwarka" method="get">
<h2>Filtr cen</h2>
<table class="formularz">
<tr><td><label for="min_price">Cena minimalna:</label></td>
<td><input type="number" name="min_price" value="${param.min_price}"></td></tr>
<tr><td><label for="max_price">Cena maksymalna:</label></td>
<td><input type="number" name="max_price" value="${param.max_price}"></td></tr>
<tr><td><button>Szukaj</button></td></tr>
</table>
</form>
<jsp:setProperty name="productBean" property="minPrice" param="min_price"/>
<jsp:setProperty name="productBean" property="maxPrice" param="max_price"/>
<c:forEach var="p" items="${productBean.filteredProducts}">
<div class="product">
<h3>${p.productName}</h3>
<div class="price">Cena: ${p.price}</div>
<div class="price">VAT ${p.vat * 100}%</div>
<c:if test="${not empty p.description}">
<p>${p.description}</p>
</c:if>
</div>
</c:forEach>
</body>
</html>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="${jstl.uri}core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista produktów 7</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Lista produktów - wersja 7</h1>
<jsp:useBean id="bean" class="sklep.web.ProductBean"/>
<form id="wyszukiwarka" method="get">
<h2>Filtr cen</h2>
<table class="formularz">
<tr><td><label for="min_price">Cena minimalna:</label></td>
<td><input type="number" name="min_price" value="${param.min_price}"></td></tr>
<tr><td><label for="max_price">Cena maksymalna:</label></td>
<td><input type="number" name="max_price" value="${param.max_price}"></td></tr>
<tr><td><button>Szukaj</button></td></tr>
</table>
</form>
<div>Produkty o cenach
<c:if test="${not empty(param.min_price)}">
od ${param.min_price}
</c:if>
<c:if test="${not empty(param.max_price)}">
do ${param.max_price}
</c:if>
</div>
<jsp:setProperty name="bean" property="minPrice" param="min_price"/>
<jsp:setProperty name="bean" property="maxPrice" param="max_price"/>
<c:forEach var="p" items="${bean.filteredProducts}">
<div class="product">
<img class="photo" src="photo?productId=${p.productId}" alt=""/>
<h3>${p.productName}</h3>
<div class="price">Cena: ${p.price}</div>
<div class="price">VAT ${p.vat * 100}%</div>
<c:if test="${not empty p.description}">
<p>${p.description}</p>
</c:if>
</div>
</c:forEach>
</body>
</html>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="${jstl.uri}core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista produktów 8</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Lista produktów - wersja 8</h1>
<jsp:useBean id="bean" class="sklep.web.ProductBean"/>
<div class="koszyk">
<h4>Koszyk</h4>
<ul>
<%-- Zauważmy, że dla obiektu koszyk nie wykonujemy już useBean.
Po prostu zakładamy, że jest obecny (w sesji). Gdyby go nie było, to pętla się nie wykona. --%>
<c:forEach var="elm" items="${basket.elements}">
<li>${elm.productName} (${elm.quantity}) za <b>${elm.value}</b></li>
</c:forEach>
</ul>
<p class="total">Do zapłaty: ${basket.totalValue}</p>
</div>
<form id="wyszukiwarka" method="get">
<h2>Filtr cen</h2>
<table class="formularz">
<tr><td><label for="min_price">Cena minimalna:</label></td>
<td><input type="number" name="min_price" value="${param.min_price}"></td></tr>
<tr><td><label for="max_price">Cena maksymalna:</label></td>
<td><input type="number" name="max_price" value="${param.max_price}"></td></tr>
<tr><td><button>Szukaj</button></td></tr>
</table>
</form>
<div>Produkty o cenach
<c:if test="${not empty(param.min_price)}">
od ${param.min_price}
</c:if>
<c:if test="${not empty(param.max_price)}">
do ${param.max_price}
</c:if>
</div>
<jsp:setProperty name="bean" property="minPrice" param="min_price"/>
<jsp:setProperty name="bean" property="maxPrice" param="max_price"/>
<c:forEach var="p" items="${bean.filteredProducts}">
<div class="product">
<img class="photo" src="photo?productId=${p.productId}" alt=""/>
<h3>${p.productName}</h3>
<div class="price">Cena: ${p.price}</div>
<div class="price">VAT ${p.vat * 100}%</div>
<c:if test="${not empty p.description}">
<p>${p.description}</p>
</c:if>
<div><a href="add_to_basket?productId=${p.productId}">dodaj do koszyka</a></div>
</div>
</c:forEach>
</body>
</html>
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="${jstl.uri}core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lista produktów 9</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Lista produktów - wersja 9</h1>
<jsp:useBean id="bean" class="sklep.web.ProductBean"/>
<div class="koszyk">
<h4>Koszyk</h4>
<ul>
<%-- Zauważmy, że dla obiektu koszyk nie wykonujemy już useBean.
Po prostu zakładamy, że jest obecny (w sesji). Gdyby go nie było, to pętla się nie wykona. --%>
<c:forEach var="elm" items="${basket.elements}">
<li>${elm.productName} (${elm.quantity}) za <b>${elm.value}</b></li>
</c:forEach>
</ul>
<p class="total">Do zapłaty: ${basket.totalValue}</p>
</div>
<form id="wyszukiwarka" method="get">
<h2>Filtr cen</h2>
<table class="formularz">
<tr><td><label for="min_price">Cena minimalna:</label></td>
<td><input type="number" name="min_price" value="${param.min_price}"></td></tr>
<tr><td><label for="max_price">Cena maksymalna:</label></td>
<td><input type="number" name="max_price" value="${param.max_price}"></td></tr>
<tr><td><button>Szukaj</button></td></tr>
</table>
</form>
<div>Produkty o cenach
<c:if test="${not empty(param.min_price)}">
od ${param.min_price}
</c:if>
<c:if test="${not empty(param.max_price)}">
do ${param.max_price}
</c:if>
</div>
<jsp:setProperty name="bean" property="minPrice" param="min_price"/>
<jsp:setProperty name="bean" property="maxPrice" param="max_price"/>
<c:forEach var="p" items="${bean.filteredProducts}">
<div class="product">
<img class="photo" src="photo?productId=${p.productId}" alt=""/>
<h3>${p.productName}</h3>
<div class="price">Cena: ${p.price}</div>
<div class="price">VAT ${p.vat * 100}%</div>
<c:if test="${not empty p.description}">
<p>${p.description}</p>
</c:if>
<div class="action"><a href="add_to_basket?productId=${p.productId}">dodaj do koszyka</a></div>
<div class="action"><a href="edit_product?productId=${p.productId}">edytuj</a></div>
<div class="action"><a href="upload_photo.jsp?productId=${p.productId}">zmień zdjęcie</a></div>
</div>
</c:forEach>
<div class="action"><a href="edit_product">Dodaj nowy produkt</a></div>
<p>Wersja JSTL: <code>${jstl.uri}</code></p>
</body>
</html>
body {
background-color: #FFFFDD;
font-family: 'Arial', sans-serif;
}
/* komentarz w CSS */
h4 {
text-align: center;
}
h2, h3, h4 {
margin-top: 0;
}
.product {
border: solid 2px blue;
margin: 1em auto 1em 50px;
padding: 1em;
background-color: white;
width: 800px;
min-height: 230px;
clear: right;
}
.koszyk {
position: fixed;
right: 0;
top: 0;
width: 300px;
height: 400px;
background-color: white;
border: outset 3px green;
}
#wyszukiwarka {
background-color: #AAEEFF;
width: 800px;
border: 2px black solid;
margin: 1em 400px 1em 50px;
padding: 1em;
}
.error {
background-color: #FFFFFF;
border: 6px double red;
margin: 20px;
padding: 10px;
color: red;
}
.photo {
display: block;
float: right;
max-width: 300px;
max-height: 200px;
margin: 5px;
}
.description {
font-size: smaller;
font-style: italic;
}
div.action {
font-size: smaller;
font-family: 'Arial', sans-serif;
font-weight: bold;
background-color: #DDDDDD;
border: 2px #444466 outset;
padding: 6px;
margin: 4px auto 4px 4px;
max-width: 200px;
}
.action:hover {
background-color: #EEEEEE;
border: 2px #4455CC outset;
}
.action:active {
background-color: #EEEEEE;
border: 2px #CC4455 inset;
}
.action a {
display: inline-block;
color: inherit;
text-decoration: none;
width: 100%;
}
.action a:hover {
color: #0000CC;
}
<%@page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Edycja zdjęcia</title>
<link rel="stylesheet" type="text/css" href="styl.css">
</head>
<body>
<h1>Wgraj zdjęcie produktu</h1>
<div>Produkt nr <strong>${param.productId}</strong></div>
<div>Aktualne zdjęcie:<br/>
<img src="photo?productId=${param.productId}" alt="Brak zdjęcia">
</div>
<%-- action powoduje, że zapytanie z formularza jest wysyłane pod podany adres, a nie bieżący.
Aby wysłać zawartość pliku (a nie tylko jego nazwę), należy ustawić enctype jak poniżej.
Sam plik to pole formularza typu file; oprócz niego mogą być inne zwykłe pola.
Odpowiednio trzeba to też obsłużyć w serwlecie - patrz klasa DoUploadPhoto.
--%>
<form id="photo-form" method="post" action="doUploadPhoto" enctype="multipart/form-data">
<input type="hidden" name="productId" value="${param.productId}">
<label for="plik">Wybierz plik ze zdjęciem</label>
<input type="file" name="plik" accept="image/jpeg">
<br>
<button>Wyślij</button>
</form>
<p>[<a href="products9.jsp">powrót do listy produktów</a>]</p>
<p>[<a href="index.html">powrót do spisu treści</a>]</p>
</body>
</html>
<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>soap_api</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>jakarta.xml.ws</groupId>
<artifactId>jakarta.xml.ws-api</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package sklep.soap;
import java.math.BigDecimal;
import java.util.List;
import jakarta.jws.WebParam;
import jakarta.jws.WebResult;
import jakarta.jws.WebService;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Customer;
import sklep.model.Order;
import sklep.model.Product;
// Na taki interfejs mówi się "service endpoint interface" (SEI)
@WebService(name="Sklep",
targetNamespace=Sklep.NAMESPACE)
public interface Sklep {
static final String NAMESPACE = "http://sklep.alx.pl/";
@WebResult(name="product")
List<Product> wszystkieProdukty() throws DBException;
@WebResult(name="product")
Product produktWgId(@WebParam(name="id") int productId) throws DBException, RecordNotFound;
@WebResult(name="product")
List<Product> produktyWgCen(
@WebParam(name="min") BigDecimal minPrice,
@WebParam(name="max") BigDecimal maxPrice) throws DBException;
void zapiszProdukt(@WebParam(name="product") Product product) throws DBException;
@WebResult(name="order")
Order zamowienieWgId(@WebParam(name="id") int orderId) throws DBException, RecordNotFound;
@WebResult(name="order")
public List<Order> zamowieniaKlienta(@WebParam(name="email") String email) throws DBException;
@WebResult(name="customer")
Customer klient(@WebParam(name="email") String email) throws DBException, RecordNotFound;
@WebResult(name="bytes")
byte[] foto(@WebParam(name="id") int productId) throws DBException, RecordNotFound;
}
\ No newline at end of file
<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>soap_klient</artifactId>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>soap_api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>4.0.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package sklep.klient;
import java.util.List;
import sklep.exn.DBException;
import sklep.model.Product;
import sklep.soap.Sklep;
public class Klient1_WszystkieProdukty {
public static void main(String[] args) {
System.out.println("Startujemy");
SklepService service = new SklepService();
Sklep sklep = service.getSklepPort();
System.out.println("Mam obiekt proxy: " + sklep);
try {
List<Product> produkty = sklep.wszystkieProdukty();
System.out.println("Odczytano " + produkty.size() + " produktów:");
for (Product product : produkty) {
System.out.println(product.getProductName() + " za cenę " + product.getPrice());
}
} catch (DBException e) {
e.printStackTrace();
}
System.out.println("Koniec");
}
}
package sklep.klient;
import java.util.Scanner;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
import sklep.soap.Sklep;
public class Klient2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
SklepService service = new SklepService();
Sklep sklep = service.getSklepPort();
while(true) {
System.out.println("Podaj ID produktu lub 0, aby zakończyć");
int id = scanner.nextInt();
if(id == 0) break;
try {
Product product = sklep.produktWgId(id);
System.out.println("Znaleziono produkt: " + product.getProductName() + " za cenę " + product.getPrice());
if(product.getDescription() != null) {
System.out.println("Opis: " + product.getDescription());
}
} catch (RecordNotFound e) {
System.out.println("Nie ma takiego rekordu");
} catch (DBException e) {
System.out.println("Inny błąd: " + e);
e.printStackTrace();
}
}
}
}
package sklep.klient;
import java.math.BigDecimal;
import java.util.Locale;
import java.util.Scanner;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
import sklep.soap.Sklep;
public class Klient3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useLocale(Locale.US);
SklepService service = new SklepService();
Sklep sklep = service.getSklepPort();
while(true) {
System.out.println("Podaj ID produktu lub 0, aby zakończyć");
int id = scanner.nextInt();
if(id == 0) break;
try {
Product product = sklep.produktWgId(id);
System.out.println("Znaleziono produkt: " + product.getProductName() + " za cenę " + product.getPrice());
if(product.getDescription() != null) {
System.out.println("Opis: " + product.getDescription());
}
boolean czyCosSieZmienilo = false;
System.out.println("Podaj zmianę ceny: ");
BigDecimal zmiana = scanner.nextBigDecimal();
if(zmiana.compareTo(BigDecimal.ZERO) != 0) {
product.setPrice(product.getPrice().add(zmiana));
czyCosSieZmienilo = true;
}
scanner.nextLine();
System.out.println("Podaj nową nazwę (enter, aby nie zmieniać): ");
String nazwa = scanner.nextLine();
if(!nazwa.isEmpty()) {
product.setProductName(nazwa);
czyCosSieZmienilo = true;
}
System.out.println("Podaj nowy opis (enter, aby nie zmieniać): ");
String opis = scanner.nextLine();
if(!opis.isEmpty()) {
product.setDescription(opis);
czyCosSieZmienilo = true;
}
if(czyCosSieZmienilo) {
sklep.zapiszProdukt(product);
System.out.println("Zapisano zmiany");
}
System.out.println();
} catch (RecordNotFound e) {
System.out.println("Nie ma takiego rekordu");
} catch (DBException e) {
System.out.println("Inny błąd: " + e);
e.printStackTrace();
}
}
}
}
package sklep.klient;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Product;
import sklep.soap.Sklep;
public class OknoSoap {
private Sklep proxy;
private JFrame frame;
private JTextField txtTytul;
private JTextField txtMarka;
private JTextField txtCena;
private JSpinner spinner;
private JLabel lblFoto;
private Product biezacyProdukt;
private ImageIcon ikonaZeZdjeciem;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
OknoSoap window = new OknoSoap();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public OknoSoap() {
SklepService serwis = new SklepService();
proxy = serwis.getSklepPort();
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 841, 801);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
spinner = new JSpinner();
spinner.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
wyswietl();
}
});
spinner.setFont(new Font("Dialog", Font.BOLD, 22));
JLabel lblPodajIdOgoszenia = new JLabel("Podaj ID ogłoszenia");
lblPodajIdOgoszenia.setFont(new Font("Dialog", Font.PLAIN, 20));
JButton btnWywietl = new JButton("Wyświetl");
btnWywietl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
wyswietl();
}
});
btnWywietl.setFont(new Font("Dialog", Font.BOLD, 20));
JPanel panel = new JPanel();
lblFoto = new JLabel("FOTO");
GroupLayout groupLayout = new GroupLayout(frame.getContentPane());
groupLayout.setHorizontalGroup(
groupLayout.createParallelGroup(Alignment.TRAILING)
.addGroup(groupLayout.createSequentialGroup()
.addContainerGap()
.addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
.addComponent(lblFoto, GroupLayout.DEFAULT_SIZE, 817, Short.MAX_VALUE)
.addGroup(groupLayout.createSequentialGroup()
.addComponent(lblPodajIdOgoszenia)
.addPreferredGap(ComponentPlacement.UNRELATED)
.addComponent(spinner, GroupLayout.PREFERRED_SIZE, 72, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED, 437, Short.MAX_VALUE)
.addComponent(btnWywietl))
.addComponent(panel, GroupLayout.DEFAULT_SIZE, 817, Short.MAX_VALUE))
.addContainerGap())
);
groupLayout.setVerticalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGroup(groupLayout.createSequentialGroup()
.addContainerGap()
.addGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
.addGroup(groupLayout.createSequentialGroup()
.addGroup(groupLayout.createParallelGroup(Alignment.BASELINE)
.addComponent(lblPodajIdOgoszenia)
.addComponent(spinner, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addGap(20))
.addGroup(groupLayout.createSequentialGroup()
.addComponent(btnWywietl)
.addPreferredGap(ComponentPlacement.UNRELATED)))
.addComponent(panel, GroupLayout.PREFERRED_SIZE, 155, GroupLayout.PREFERRED_SIZE)
.addGap(18)
.addComponent(lblFoto, GroupLayout.DEFAULT_SIZE, 523, Short.MAX_VALUE)
.addContainerGap())
);
JLabel lblTytu = new JLabel("Nazwa");
lblTytu.setFont(new Font("Dialog", Font.PLAIN, 18));
txtTytul = new JTextField();
txtTytul.setFont(new Font("Dialog", Font.BOLD, 16));
txtTytul.setColumns(10);
JLabel lblMarka = new JLabel("Opis");
lblMarka.setFont(new Font("Dialog", Font.PLAIN, 18));
txtMarka = new JTextField();
txtMarka.setFont(new Font("Dialog", Font.BOLD, 16));
txtMarka.setColumns(10);
JLabel lblCena = new JLabel("Cena");
lblCena.setFont(new Font("Dialog", Font.PLAIN, 18));
txtCena = new JTextField();
txtCena.setFont(new Font("Dialog", Font.BOLD, 16));
txtCena.setColumns(10);
GroupLayout gl_panel = new GroupLayout(panel);
gl_panel.setHorizontalGroup(
gl_panel.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel.createSequentialGroup()
.addContainerGap()
.addGroup(gl_panel.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel.createSequentialGroup()
.addGroup(gl_panel.createParallelGroup(Alignment.TRAILING, false)
.addComponent(lblMarka, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(lblTytu, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 152, Short.MAX_VALUE))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(gl_panel.createParallelGroup(Alignment.LEADING)
.addComponent(txtMarka, GroupLayout.DEFAULT_SIZE, 623, Short.MAX_VALUE)
.addComponent(txtTytul, GroupLayout.DEFAULT_SIZE, 623, Short.MAX_VALUE)))
.addGroup(Alignment.TRAILING, gl_panel.createSequentialGroup()
.addComponent(lblCena)
.addGap(128)
.addComponent(txtCena, GroupLayout.DEFAULT_SIZE, 623, Short.MAX_VALUE)))
.addContainerGap())
);
gl_panel.setVerticalGroup(
gl_panel.createParallelGroup(Alignment.LEADING)
.addGroup(gl_panel.createSequentialGroup()
.addContainerGap()
.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
.addComponent(lblTytu)
.addComponent(txtTytul, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(ComponentPlacement.UNRELATED)
.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
.addComponent(lblMarka)
.addComponent(txtMarka, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addGap(18)
.addGroup(gl_panel.createParallelGroup(Alignment.BASELINE)
.addComponent(txtCena, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(lblCena))
.addContainerGap(44, Short.MAX_VALUE))
);
panel.setLayout(gl_panel);
frame.getContentPane().setLayout(groupLayout);
}
// jest wywoływane przez Swinga po kliknięciu guzika itp.
// ta metoda jest wykonywana w wątku EDT
protected void wyswietl() {
int idOgloszenia = (Integer)spinner.getValue();
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// to wykona się w oddzielnym wątku,
// nie zablokuj okna, ale tutaj nie powinniśmy korzystać z elementów okna
pobierzDane(idOgloszenia);
return null;
}
@Override
protected void done() {
// tu powinniśmy wpisać polecenia dotyczące okna, które mają być wykonane przez EDT po zakończeniu operacji
uaktualnijWidok();
}
};
worker.execute();
}
private void pobierzDane(int id) {
// ma być wykonane w tle
try {
ikonaZeZdjeciem = null;
biezacyProdukt = null;
biezacyProdukt = proxy.produktWgId(id);
try {
byte[] bajtyZeZdjeciem = proxy.foto(id);
if(bajtyZeZdjeciem != null) {
ikonaZeZdjeciem = new ImageIcon(bajtyZeZdjeciem);
}
} catch (RecordNotFound e) {
// zdjecie zostaje nullem
}
} catch (DBException e) {
e.printStackTrace();
} catch (RecordNotFound e) {
}
}
private void uaktualnijWidok() {
// ma być wykonane przez okno (czyli wątek EDT)
if(biezacyProdukt != null) {
txtTytul.setText(biezacyProdukt.getProductName());
txtMarka.setText(biezacyProdukt.getDescription());
txtCena.setText(String.valueOf(biezacyProdukt.getPrice()));
} else {
txtTytul.setText("");
txtMarka.setText("");
txtCena.setText("");
}
lblFoto.setIcon(ikonaZeZdjeciem);
}
}
package sklep.klient;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import jakarta.xml.ws.Service;
import jakarta.xml.ws.WebEndpoint;
import jakarta.xml.ws.WebServiceClient;
import jakarta.xml.ws.WebServiceFeature;
import sklep.soap.Sklep;
@WebServiceClient(name="SklepService",
wsdlLocation=SklepService.ADRES_WSDL,
targetNamespace=Sklep.NAMESPACE)
public class SklepService extends Service {
final static String ADRES_WSDL = "http://localhost:8080/soap_serwer/SklepService?wsdl";
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName(Sklep.NAMESPACE, "SklepService");
public final static QName SklepPort = new QName(Sklep.NAMESPACE, "SklepPort");
static {
URL url = null;
try {
url = new URL(ADRES_WSDL);
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from " + ADRES_WSDL);
}
WSDL_LOCATION = url;
}
public SklepService(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public SklepService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public SklepService() {
super(WSDL_LOCATION, SERVICE);
}
public SklepService(WebServiceFeature... features) {
super(WSDL_LOCATION, SERVICE, features);
}
public SklepService(URL wsdlLocation, WebServiceFeature... features) {
super(wsdlLocation, SERVICE, features);
}
public SklepService(URL wsdlLocation, QName serviceName, WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
@WebEndpoint(name = "SklepPort")
public Sklep getSklepPort() {
return super.getPort(SklepPort, Sklep.class);
}
@WebEndpoint(name = "SklepPort")
public Sklep getSklepPort(WebServiceFeature... features) {
return super.getPort(SklepPort, Sklep.class, features);
}
}
<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>soap_serwer</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>soap_api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>baza</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
</build>
</project>
package sklep.soap;
import java.math.BigDecimal;
import java.util.List;
import jakarta.jws.WebParam;
import jakarta.jws.WebResult;
import jakarta.jws.WebService;
import jakarta.xml.ws.soap.MTOM;
import sklep.db.CustomerDAO;
import sklep.db.DBConnection;
import sklep.db.OrderDAO;
import sklep.db.ProductDAO;
import sklep.exn.DBException;
import sklep.exn.RecordNotFound;
import sklep.model.Customer;
import sklep.model.Order;
import sklep.model.Product;
import sklep.photo.PhotoUtil;
@WebService(endpointInterface="sklep.soap.Sklep",
targetNamespace=Sklep.NAMESPACE,
serviceName="SklepService",
portName="SklepPort")
@MTOM
public class SklepImpl implements Sklep {
public List<Product> wszystkieProdukty() throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.readAll();
}
}
public Product produktWgId(int productId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findById(productId);
}
}
public List<Product> produktyWgCen(
BigDecimal minPrice,
BigDecimal maxPrice) throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
return productDAO.findByPrice(minPrice, maxPrice);
}
}
public void zapiszProdukt(Product product) throws DBException {
try(DBConnection db = DBConnection.open()) {
ProductDAO productDAO = db.productDAO();
productDAO.save(product);
db.commit();
}
}
public Order zamowienieWgId(int orderId) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
OrderDAO orderDAO = db.orderDAO();
return orderDAO.findById(orderId);
}
}
public List<Order> zamowieniaKlienta(String email) throws DBException {
try(DBConnection db = DBConnection.open()) {
OrderDAO orderDAO = db.orderDAO();
return orderDAO.customerOrders(email);
}
}
public Customer klient(String email) throws DBException, RecordNotFound {
try(DBConnection db = DBConnection.open()) {
CustomerDAO customerDAO = db.customerDAO();
return customerDAO.findByEmail(email);
}
}
public byte[] foto(int productId) throws DBException, RecordNotFound {
return PhotoUtil.readBytes(productId);
}
}
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