ПИА/К 2022
Kolokvijum 2022. godine za RTI održan je 17. aprila i trajao je dva sata. Koristila se tehnologija Java Server Faces sa MySQL bazom podataka, dostupnim materijalima sa predavanja, dodatnim klasama za rad sa bazom (DB.java
) i sesijama (SessionUtils.java
), datumima (DateUtils.java
) kao i skriptom za pravljenje baze. Postavka roka je dostupna sa stranice predmeta.
Iako se kolokvijum radio na JDK verziji 8, ispod je dato rešenje korišćenjem JDK verzije 11.
Postavka
Napraviti sledeću mini internet aplikaciju za agenciju za registraciju automobila „Fića“. Internet aplikaciju realizovati koristeći tehnologiju Java Server Faces.
Na početnoj strani aplikacije, napraviti HTML formu, preko koje mogu da se prijavljuju korisnici sistema, a to su kupci i zaposleni. Korisnici treba da imaju mogućnost unošenja korisničkog imena i lozinke i da pomoću radio dugmeta odaberu da li se prijavljuju kao kupci ili zaposleni. U slučaju ispravno unetih podataka, korisniku treba omogućiti rad sa ostatkom sistema (za svaki tip korisnika treba prikazati posebnu početnu veb stranicu nakon prijavljivanja). Ukoliko korisnik ne unese neki od podataka ili unese pogrešne podatke, potrebno je ispisati poruku greške slovima crvene boje, sa mogućnošću ispravljanja greške u formi. Po uspešnoj prijavi u sistem, korisniku dati i opciju da se odjavi. [7 poena]
Kupac nakon uspešnog prijavljivanja, vidi svoje lične podatke (ime i prezime) i formu koja predstavlja kalkulator registracije automobila. U formi iz padajuće se bira marka automobila (padajuću listu popuniti podacima iz baze, tabela "Automobili"), unosi se datum prve registracije automobila (datum mora biti u prošlosti) i snaga motora izražena u kW (ceo broj, od 50, pa naviše). Pomoću polja za štikliranje (checkbox) dodatno se bira da li uz registraciju automobila kupac želi da izvrši tehnički pregled i/ili kupi polisu osiguranja. Ispod forme postoji dugme Izračunaj ponudu za registraciju. Pritiskom na to dugme, kupcu se otvara nova stranica na kojoj mu je napisana ponuda agencije za odabrane opcije iz forme. [6 poena] Ponuda se računa tako što se na osnovni iznos koji predstavlja iznos registracije prema marki automobila, dodaje 5000 dinara za vozila od čije je prve registracije prošlo više od 4 godine, 3000 dinara za vozila čija je snaga između 50 kW i 100 kW, 6000 dinara za vozila čija je snaga veća od 100 kW. Tehnički pregled se naplaćuje 6000 dinara, a polisa osiguranja 10000 dinara. [4 poena] Ukoliko je ponuda manja od 25000 dinara, obojiti je zelenom bojom. [2 poena] Pored svoje ponude kupac vidi i dva dugmeta Prihvati i Nazad. Pritiskom na dugme Prihvati zahtev se upisuje u bazu. Prilikom pamćenja zahteva pamti se korisničko ime kupca, marka automobila, datum kreiranja zahteva i iznos ponude za registraciju. Ispisati poruku da je uspešno sačuvan zahtev. [5 poena] Pritiskom na dugme Nazad, kupac se vraća na stranicu sa formom za unos podataka, u kojoj treba da budu sačuvani svi podaci koje je uneo u prethodnom koraku. [1 poen]
Zaposleni nakon uspešnog prijavljivanja vidi tabelarni prikaz svih zahteva. U tabeli prikazati korisničko ime kupca, naziv marke automobila, datum kreiranja zahteva i iznos za plaćanje registracije. [6 poena] Zaposleni ima opciju da obriše zahtev, pritiskom na dugme Obriši, čime se zahtev briše iz baze. Nakon brisanja, osvežiti stranicu. [4 poena]
Rešenje
Stranice
index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Dobrodošli</title>
<h:outputStylesheet name="css/main.css"></h:outputStylesheet>
</h:head>
<h:body>
<h:form>
<p>
<label for="username">Korisničko ime:</label>
<h:inputText value="#{lmb.username}" id="username"></h:inputText>
</p>
<p>
<label for="password">Lozinka:</label>
<h:inputSecret value="#{lmb.password}" id="password"></h:inputSecret>
</p>
<p>
<h:selectOneRadio value="#{lmb.group}">
<f:selectItem itemLabel="Kupac" itemValue="kupac"></f:selectItem>
<f:selectItem itemLabel="Zaposleni" itemValue="zaposleni"></f:selectItem>
</h:selectOneRadio>
</p>
<p><h:commandButton action="#{lmb.login()}" value="Uloguj se"></h:commandButton></p>
<p><h:outputText value="#{lmb.error}" class="error"></h:outputText></p>
</h:form>
</h:body>
</html>
kupac.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Kupac</title>
<h:outputStylesheet name="css/main.css"></h:outputStylesheet>
</h:head>
<h:body>
<h:form>
<p>Dobrodošli, <h:outputText value="#{kmb.name}"></h:outputText> <h:outputText value="#{kmb.surname}"></h:outputText>.</p>
<h:commandButton action="#{lmb.logout()}" value="Odjavi se"></h:commandButton>
<h2>Kalkulator</h2>
<p>
<label for="marka">Marka:</label>
<h:selectOneMenu value="#{kmb.marka}" id="marka">
<f:selectItems value="#{kmb.automobili}"></f:selectItems>
</h:selectOneMenu>
</p>
<p>
<label for="datum">Datum (yyyy-MM-dd):</label>
<h:inputText value="#{kmb.datum}" id="datum"></h:inputText>
</p>
<p>
<label for="datum">Snaga:</label>
<h:inputText value="#{kmb.snaga}" id="snaga"></h:inputText>
</p>
<p>
<ul>
<li>Tehnički pregled: <h:selectBooleanCheckbox value="#{kmb.tehnicki}"></h:selectBooleanCheckbox></li>
<li>Polisa osiguranja: <h:selectBooleanCheckbox value="#{kmb.polisa}"></h:selectBooleanCheckbox></li>
</ul>
</p>
<p><h:commandButton action="#{kmb.calculate()}" value="Izračunaj ponudu za registraciju"></h:commandButton></p>
<p><h:outputText value="#{kmb.error}" class="error"></h:outputText></p>
<p><h:outputText value="#{kmb.success}" class="green"></h:outputText></p>
</h:form>
</h:body>
</html>
ponuda.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Ponuda</title>
<h:outputStylesheet name="css/main.css"></h:outputStylesheet>
</h:head>
<h:body>
<h:form>
<p>Ponuda: <h:outputText class="#{(kmb.ponuda lt 25000) ? 'green' : ''}" value="#{kmb.ponuda}"></h:outputText></p>
<p>
<h:commandButton action="#{kmb.accept()}" value="Prihvati"></h:commandButton>
<h:commandButton action="#{kmb.back()}" value="Nazad"></h:commandButton>
</p>
</h:form>
</h:body>
</html>
zaposleni.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Zaposleni</title>
<h:outputStylesheet name="css/main.css"></h:outputStylesheet>
</h:head>
<h:body>
<h:form>
<h:commandButton action="#{lmb.logout()}" value="Odjavi se"></h:commandButton>
<h2>Zahtevi</h2>
<h:dataTable value="#{zmb.requests}" var="r">
<h:column>
<f:facet name="header">Kupac</f:facet>
#{r.kupac}
</h:column>
<h:column>
<f:facet name="header">Marka</f:facet>
#{r.marka}
</h:column>
<h:column>
<f:facet name="header">Datum</f:facet>
#{r.datum}
</h:column>
<h:column>
<f:facet name="header">Iznos</f:facet>
#{r.iznos}
</h:column>
<h:column>
<h:commandButton action="#{zmb.delete(r)}" value="Obriši"></h:commandButton>
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
Zrna
LoginManagedBean
package rs.ac.bg.etf.pia.fica2022.beans;
import jakarta.inject.Named;
import jakarta.enterprise.context.RequestScoped;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import rs.ac.bg.etf.pia.fica2022.util.DB;
import rs.ac.bg.etf.pia.fica2022.util.SessionUtils;
@Named(value = "lmb")
@RequestScoped
public class LoginManagedBean {
private String username;
private String password;
private String group;
private String error;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String login() {
if (username.isEmpty() || password.isEmpty() || group.isEmpty()) {
error = "Potrebno je uneti sve podatke.";
return null;
}
DB inst = DB.getInstance();
Connection db = inst.getConnection();
try (
PreparedStatement stmt = db.prepareStatement("SELECT * FROM korisnici WHERE korisnicko_ime = ? AND lozinka = ? AND tip = ?");
) {
stmt.setString(1, username);
stmt.setString(2, password);
stmt.setString(3, group);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
SessionUtils.setAttribute("username", username);
SessionUtils.setAttribute("name", rs.getString("ime"));
SessionUtils.setAttribute("surname", rs.getString("prezime"));
return group + ".xhtml?faces-redirect=true";
} else {
error = "Korisničko ime, lozinka ili tip nisu tačno uneti.";
}
}
} catch (SQLException ex) {
Logger.getLogger(LoginManagedBean.class.getName()).log(Level.SEVERE, null, ex);
} finally {
inst.putConnection(db);
}
return null;
}
public String logout() {
SessionUtils.getSession().removeAttribute("username");
SessionUtils.getSession().removeAttribute("name");
SessionUtils.getSession().removeAttribute("surname");
return "index.xhtml?faces-redirect=true";
}
}
KupacManagedBean
package rs.ac.bg.etf.pia.fica2022.beans;
import rs.ac.bg.etf.pia.fica2022.entities.Automobil;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.SessionScoped;
import jakarta.inject.Named;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import rs.ac.bg.etf.pia.fica2022.util.DB;
import rs.ac.bg.etf.pia.fica2022.util.DateUtil;
import rs.ac.bg.etf.pia.fica2022.util.SessionUtils;
@Named(value = "kmb")
@SessionScoped
public class KupacManagedBean implements Serializable {
private int ponuda;
private String marka;
private String name;
private String surname;
private List<Automobil> automobili = new ArrayList<>();
private String datum;
private String snaga;
private boolean tehnicki;
private boolean polisa;
private String error;
private String success;
private int idA;
public int getPonuda() {
return ponuda;
}
public void setPonuda(int ponuda) {
this.ponuda = ponuda;
}
public String getMarka() {
return marka;
}
public void setMarka(String marka) {
this.marka = marka;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public List<Automobil> getAutomobili() {
return automobili;
}
public void setAutomobili(List<Automobil> automobili) {
this.automobili = automobili;
}
public String getDatum() {
return datum;
}
public void setDatum(String datum) {
this.datum = datum;
}
public String getSnaga() {
return snaga;
}
public void setSnaga(String snaga) {
this.snaga = snaga;
}
public boolean isTehnicki() {
return tehnicki;
}
public void setTehnicki(boolean tehnicki) {
this.tehnicki = tehnicki;
}
public boolean isPolisa() {
return polisa;
}
public void setPolisa(boolean polisa) {
this.polisa = polisa;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getSuccess() {
return success;
}
public void setSuccess(String success) {
this.success = success;
}
@PostConstruct
public void retrieveData() {
name = SessionUtils.getAttribute("name").toString();
surname = SessionUtils.getAttribute("surname").toString();
DB inst = DB.getInstance();
Connection db = inst.getConnection();
try (
Statement stmt = db.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM automobili");
) {
while (rs.next()) {
Automobil a = new Automobil();
a.setCena(rs.getInt("cena_registracije"));
a.setMarka(rs.getString("marka"));
a.setId(rs.getInt("idA"));
automobili.add(a);
}
} catch (SQLException ex) {
Logger.getLogger(LoginManagedBean.class.getName()).log(Level.SEVERE, null, ex);
} finally {
inst.putConnection(db);
}
}
public String calculate() {
success = "";
ponuda = 0;
Automobil auto = automobili.stream().filter(a -> a.getMarka().equals(marka)).findFirst().get();
ponuda += auto.getCena();
try {
Date registracija = DateUtil.stringToUtil(datum, "yyyy-MM-dd");
Date sada = DateUtil.currentUtil();
long diff = sada.getTime() - registracija.getTime();
if (diff < 0) {
error = "Datum registracije mora biti u prošlosti.";
return null;
}
long godine = diff / 1000 / 60 / 60 / 24 / 365;
if (godine > 4) {
ponuda += 5000;
}
int snagaBroj = Integer.parseInt(snaga);
if (snagaBroj < 50) {
error = "Snaga mora biti barem 50kW.";
return null;
} else if (snagaBroj < 100) {
ponuda += 3000;
} else {
ponuda += 6000;
}
if (tehnicki) {
ponuda += 6000;
}
if (polisa) {
ponuda += 10000;
}
idA = auto.getId();
} catch (ParseException ex) {
error = "Datum nije u zadatom formatu";
return null;
} catch (NumberFormatException ex) {
error = "Snaga mora biti ceo broj.";
return null;
}
return "ponuda.xhtml?faces-redirect=true";
}
public String accept() {
DB inst = DB.getInstance();
Connection db = inst.getConnection();
try (
PreparedStatement stmt = db.prepareStatement("INSERT INTO zahtevi (kupac, automobil, datum, iznos) VALUES (?, ?, NOW(), ?)");
) {
stmt.setString(1, SessionUtils.getAttribute("username").toString());
stmt.setInt(2, idA);
stmt.setInt(3, ponuda);
stmt.execute();
error = "";
success = "Uspešno sačuvan zahtev.";
} catch (SQLException ex) {
Logger.getLogger(LoginManagedBean.class.getName()).log(Level.SEVERE, null, ex);
} finally {
inst.putConnection(db);
}
marka = "";
datum = "";
snaga = "";
polisa = false;
tehnicki = false;
return "kupac.xhtml?faces-redirect=true";
}
public String back() {
return "kupac.xhtml?faces-redirect=true";
}
}
ZaposleniManagedBean
package rs.ac.bg.etf.pia.fica2022.beans;
import rs.ac.bg.etf.pia.fica2022.entities.Zahtev;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Named;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import rs.ac.bg.etf.pia.fica2022.util.DB;
@Named(value = "zmb")
@RequestScoped
public class ZaposleniManagedBean {
private List<Zahtev> requests = new ArrayList<>();
public List<Zahtev> getRequests() {
return requests;
}
public void setRequests(List<Zahtev> requests) {
this.requests = requests;
}
@PostConstruct
public void retrieveData() {
DB inst = DB.getInstance();
Connection db = inst.getConnection();
try (
Statement stmt = db.createStatement();
ResultSet rs = stmt.executeQuery("SELECT z.datum AS datum, z.iznos AS iznos, z.kupac AS kupac, z.idZ AS id, a.marka AS marka FROM zahtevi z JOIN automobili a ON z.automobil = a.idA");
) {
while (rs.next()) {
Zahtev z = new Zahtev();
z.setDatum(rs.getString("datum"));
z.setIznos(rs.getInt("iznos"));
z.setKupac(rs.getString("kupac"));
z.setMarka(rs.getString("marka"));
z.setId(rs.getInt("id"));
requests.add(z);
}
} catch (SQLException ex) {
Logger.getLogger(LoginManagedBean.class.getName()).log(Level.SEVERE, null, ex);
} finally {
inst.putConnection(db);
}
}
public String delete(Zahtev z) {
DB inst = DB.getInstance();
Connection db = inst.getConnection();
try (
PreparedStatement stmt = db.prepareStatement("DELETE FROM zahtevi WHERE idZ = ?");
) {
stmt.setInt(1, z.getId());
stmt.execute();
} catch (SQLException ex) {
Logger.getLogger(LoginManagedBean.class.getName()).log(Level.SEVERE, null, ex);
} finally {
inst.putConnection(db);
}
return "zaposleni.xhtml?faces-redirect=true";
}
}
Entiteti
Automobil.java
package rs.ac.bg.etf.pia.fica2022.entities;
public class Automobil {
private String marka;
private int cena;
private int id;
public String getMarka() {
return marka;
}
public void setMarka(String marka) {
this.marka = marka;
}
public int getCena() {
return cena;
}
public void setCena(int cena) {
this.cena = cena;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return marka;
}
}
Zahtev.java
package rs.ac.bg.etf.pia.fica2022.entities;
public class Zahtev {
private String kupac;
private String marka;
private String datum;
private int iznos;
private int id;
public String getKupac() {
return kupac;
}
public void setKupac(String kupac) {
this.kupac = kupac;
}
public String getMarka() {
return marka;
}
public void setMarka(String marka) {
this.marka = marka;
}
public String getDatum() {
return datum;
}
public void setDatum(String datum) {
this.datum = datum;
}
public int getIznos() {
return iznos;
}
public void setIznos(int iznos) {
this.iznos = iznos;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Ostali fajlovi
main.css
.error {
color: red;
}
.green {
color: green;
}