ПИА/К 2022
Колоквијум 2022. године за РТИ одржан је 17. априла и трајао је два сата. Користила се технологија Јава Сервер Фацес са МyСQЛ базом података, доступним материјалима са предавања, додатним класама за рад са базом (DB.java
) и сесијама (SessionUtils.java
), датумима (DateUtils.java
) као и скриптом за прављење базе. Поставка рока је доступна са странице предмета.
Иако се колоквијум радио на ЈДК верзији 8, испод је дато решење коришћењем ЈДК верзије 11.
Поставка
Направити следећу мини интернет апликацију за агенцију за регистрацију аутомобила „Фића“. Интернет апликацију реализовати користећи технологију Јава Сервер Фацес.
На почетној страни апликације, направити ХТМЛ форму, преко које могу да се пријављују корисници система, а то су купци и запослени. Корисници треба да имају могућност уношења корисничког имена и лозинке и да помоћу радио дугмета одаберу да ли се пријављују као купци или запослени. У случају исправно унетих података, кориснику треба омогућити рад са остатком система (за сваки тип корисника треба приказати посебну почетну веб страницу након пријављивања). Уколико корисник не унесе неки од података или унесе погрешне податке, потребно је исписати поруку грешке словима црвене боје, са могућношћу исправљања грешке у форми. По успешној пријави у систем, кориснику дати и опцију да се одјави. [7 поена]
Купац након успешног пријављивања, види своје личне податке (име и презиме) и форму која представља калкулатор регистрације аутомобила. У форми из падајуће се бира марка аутомобила (падајућу листу попунити подацима из базе, табела "Аутомобили"), уноси се датум прве регистрације аутомобила (датум мора бити у прошлости) и снага мотора изражена у кW (цео број, од 50, па навише). Помоћу поља за штиклирање (цхецкбоx) додатно се бира да ли уз регистрацију аутомобила купац жели да изврши технички преглед и/или купи полису осигурања. Испод форме постоји дугме Израчунај понуду за регистрацију. Притиском на то дугме, купцу се отвара нова страница на којој му је написана понуда агенције за одабране опције из форме. [6 поена] Понуда се рачуна тако што се на основни износ који представља износ регистрације према марки аутомобила, додаје 5000 динара за возила од чије је прве регистрације прошло више од 4 године, 3000 динара за возила чија је снага између 50 кW и 100 кW, 6000 динара за возила чија је снага већа од 100 кW. Технички преглед се наплаћује 6000 динара, а полиса осигурања 10000 динара. [4 поена] Уколико је понуда мања од 25000 динара, обојити је зеленом бојом. [2 поена] Поред своје понуде купац види и два дугмета Прихвати и Назад. Притиском на дугме Прихвати захтев се уписује у базу. Приликом памћења захтева памти се корисничко име купца, марка аутомобила, датум креирања захтева и износ понуде за регистрацију. Исписати поруку да је успешно сачуван захтев. [5 поена] Притиском на дугме Назад, купац се враћа на страницу са формом за унос података, у којој треба да буду сачувани сви подаци које је унео у претходном кораку. [1 поен]
Запослени након успешног пријављивања види табеларни приказ свих захтева. У табели приказати корисничко име купца, назив марке аутомобила, датум креирања захтева и износ за плаћање регистрације. [6 поена] Запослени има опцију да обрише захтев, притиском на дугме Обриши, чиме се захтев брише из базе. Након брисања, освежити страницу. [4 поена]
Решење
Странице
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>
Зрна
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";
}
}
Ентитети
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;
}
}
Остали фајлови
main.css
.error {
color: red;
}
.green {
color: green;
}