Salutare, în acest articol voi scrie despre JSF (Fava server Faces). Deoarece am descoperit că o aplicație mică la prima vedere poate deveni destul de complexă, o voi structura pe 3 capitole. Si anume, baza de date, middle tier și în final partea de view, adică JSF efectiv. Niste linkuri utile despre JSF gasiti aici: Kickstart, site oficial, articol IBM , tot pe acolo gasiti mai multe articole despre JSF, Developers book. Daca citit toate astea o sa va fie mai usor sa intelegeti aplicatia, sau ati putea chiar sa nu mai cititi de aici.
O aplicație web ar trebui să arate cam asa: 
A se observa unde este Faces servlet si de asemenea faptul ca mai sunt niste tehnologii care permit unei aplicatii sa fie modulara si scalabila. Avand in vedere ca aplicatia este una pur JSF nivelul business nu poate fi reprezentat de o alta tehnologie. Asa ca o sa il scriem de manutza
. Nu e greu cand ai 2-3 clase, dar de pe la 20 de clasa in sus, ar cam trebui sa folosim un alt framework care sa ne ajute un pic cu gestionarea. Partea de baze de date nu poate fi evitata orice ai face, si aici o sa folosesc PostgreSQL Este usor de folosit, este gratis, si mai este si performanta (nu ca ar conta pt cateva tabele).
Nu am spus ce face aplicatia, nu mi se pare important atunci cand faci o aplicatie doar pentru a testa un framework, dar pentru logica celor scrise in continuare va spun ca aplicatia o sa “gestinoneze” niste scoruri la meciuri.
Sa incepem cu structura aplicatiei, aceasta asrata cam asa :

Respirati adanc… nu e asa de complicat pe cat pare, am pus clasele in multe pachete pt ca
au diferite functii si am vrut sa le regasesc usor, si sa ma obisnuiesc cu o organizare stricta. Imaginati-va cam a avea 10 convertori si 10 validatori… si asta e doar pt un proiect mediu spre mic
. Un tutorial pentru convertori si validatori il puteti vedea aici.
Sa incepem cu beanurile, acestea se afla in pacjetul jsfp si sunt niste clase cu niste campuri care sunt corespunzatoare cu campurile aferente din tabelele din baza de date. De exemplu in baza de date exista tabela score, deci am bean cu acelas nume. Beanul Score arata asa:
package jsfp;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Locale;
import jdba.ScoreService;
public class Score implements Serializable {
private Long id;
private Team hoast;
private Team guest;
private Integer score1;
private Integer score2;
private Calendar date;
@Override
public boolean equals(Object o) {
if (!(o instanceof Score)) {
return false;
} else {
Score t = (Score) o;
if (id != t.getId()) {
return false;
} else {
return true;
}
}
}
@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.id != null ? this.id.hashCode() : 0);
hash = 53 * hash + (this.hoast != null ? this.hoast.hashCode() : 0);
hash = 53 * hash + (this.guest != null ? this.guest.hashCode() : 0);
hash = 53 * hash + (this.score1 != null ? this.score1.hashCode() : 0);
hash = 53 * hash + (this.score2 != null ? this.score2.hashCode() : 0);
return hash;
}
public void persist() {
ScoreService ss = new ScoreService();
ss.insert(this);
score1 = 0;
score2 = 0;
date = Calendar.getInstance(new Locale(new Local().getLocale()));
}
// getters and setters for every field
}
IMPORTANT Suprascrierea metodelor equals si getHash este obligatorie, pt ca JSF compara obiectele atunci cand lucreaza cu ele.
Clasa Team.java arata cam asa:
package jsfp;
import java.io.Serializable;
import jdba.TeamService;
public class Team implements Serializable {
private Long id;
private String name;
private String manager;
private String coach;
private String mood;
private Integer points;
@Override
public boolean equals(Object o) {
if (!(o instanceof Team)) {
return false;
} else {
Team t = (Team) o;
if (id != t.getId()) {
return false;
} else {
return true;
}
}
}
@Override
public int hashCode() {
int hash = 7;
hash = 71 * hash + (this.id != null ? this.id.hashCode() : 0);
hash = 71 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 71 * hash + (this.manager != null ? this.manager.hashCode() : 0);
hash = 71 * hash + (this.coach != null ? this.coach.hashCode() : 0);
hash = 71 * hash + (this.mood != null ? this.mood.hashCode() : 0);
hash = 71 * hash + (this.points != null ? this.points.hashCode() : 0);
return hash;
}
public void persist() {
TeamService ts = new TeamService();
ts.insert(this);
}
// getters and setters for every field
}
Clasa Locale nu are echivalent in baza de date, este folosita doar pentru a stoca variabila de interbationalizare. Ea arata asa:
package jsfp;
public class Local {
private static String locale = new String("en");
public String setLocaleRo() {
locale = new String("ro");
return "ro";
}
public String setLocaleEn() {
locale = new String("en");
return "en";
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
}
Conectarea la baza de date am realizat-o destul de simplu si ineficient d.p.d.v al scalabilitatii, dar avand in vedere ca asta e doar o tema si nu sunt mai mult de 2-3 utilizatori se accepta
. Ideea este urmatoarea: am facut o clasa singleton DbCon care tine o conexiune la baza de date, cand am nevoie de o conexiune de oriunde din aplicatie, apelez aceasta clasa si iau o conexiune. Aceasta clasa arata asa:
package jdba;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ResourceBundle;
public class DbCon {
private static DbCon instance = null;
private DbCon() {
}
public static DbCon getInstance() {
if (instance == null) {
instance = new DbCon();
}
return instance;
}
public Connection getCon() {
try {
ResourceBundle rsb = ResourceBundle.getBundle("jdba.DatabaseProps");
Class.forName(rsb.getString("driver"));
Connection c = DriverManager.getConnection(rsb.getString("url"), rsb.getString("user"),
rsb.getString("pass"));
return c;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
Pentru a usura optinerea conexiunii am facut o clasa abstracta in care am declarat conexiunea un statement si un String ‘query’, aceasta clasa mai contine si 2 metode de deschidere si inchidere a conexiunii. Aceasta clasa arata asa:
package jdba;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public abstract class Service {
Connection c;
Statement s;
String q;
protected void open() {
try {
DbCon d = DbCon.getInstance();
c = d.getCon();
s = c.createStatement();
c.setAutoCommit(false);
} catch (SQLException ex) {
System.out.println("Error on opening connection");
ex.printStackTrace();
}
}
protected void close() {
try {
c.commit();
s.close();
c.close();
} catch (SQLException ex) {
System.out.println("Error on closing connection");
ex.printStackTrace();
}
}
}
Clasa ScoreService arata asa:
package jdba;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.Vector;
import jsfp.Score;
public class ScoreService extends Service {
public void insert(Score sc) {
open();
Date now = sc.getDate().getTime();
q = "insert into score (hoast,guest,score1,score2,date) " +
"values (" + sc.getHoast().getId() + "," +
sc.getGuest().getId() + "," +
sc.getScore1() + "," +
sc.getScore2() + ",'" +
now.getDay() + "/" + now.getMonth() + "/" + (1900 + now.getYear()) + "');";
try {
s.executeUpdate(q);
} catch (SQLException ex) {
ex.printStackTrace();
}
if (sc.getScore1() > sc.getScore2()) {
q = "update team set points = points + " + 3 + " where id = " + sc.getHoast().getId() + " ;";
} else if (sc.getScore1() < sc.getScore2()) {
q = "update team set points = points + " + 3 + " where id = " + sc.getGuest().getId() + " ;";
} else {
q = "update team set points = points + " + 1 + "where id in (" + sc.getHoast().getId() + "," + sc.getGuest().getId() + ") ;";
}
try {
s.executeUpdate(q);
} catch (SQLException ex) {
ex.printStackTrace();
}
close();
}
public Vector getAll() {
open();
Vector scores = new Vector();
q = "select hoast,guest,score1,score2,date from score";
try {
TeamService ts = new TeamService();
ResultSet rs = s.executeQuery(q);
while (rs.next()) {
Score sc = new Score();
sc.setHoast(ts.getById(rs.getLong(1)));
sc.setGuest(ts.getById(rs.getLong(2)));
sc.setScore1(rs.getInt(3));
sc.setScore2(rs.getInt(4));
String[] dts = rs.getString(5).split("/");
Calendar cl = Calendar.getInstance();
cl.set(Integer.parseInt(dts[2]), Integer.parseInt(dts[1]), Integer.parseInt(dts[0]));
sc.setDate(cl);
scores.add(sc);
}
} catch (SQLException ex) {
ex.printStackTrace();
}
close();
return scores;
}
public boolean gameDisputed(long hoast, long guest) {
boolean flag = false;
open();
System.out.println("Hoast = " + hoast + " Guest = " + guest);
q = "select * from score where hoast = " + hoast + " and guest = " + guest + ";";
try {
ResultSet rs = s.executeQuery(q);
flag = rs.next();
} catch (SQLException ex) {
ex.printStackTrace();
}
close();
return flag;
}
}
Clasa TeamService arata asa:
package jdba;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Vector;
import jsfp.Team;
public class TeamService extends Service {
public void insert(Team t) {
open();
q = "insert into team (name,manager,coach,mood,points) " +
"values ( '" + t.getName() + "'," +
"'" + t.getManager() + "'," +
"'" + t.getCoach() + "'," +
"'" + t.getMood() + "',0);";
try {
s.executeUpdate(q);
} catch (SQLException ex) {
ex.printStackTrace();
}
close();
}
public Team getByName(String nm) {
open();
Team t = null;
q = "select id,name,manager,coach,mood,points from team where name = '" + nm + "' ;";
try {
ResultSet rs = s.executeQuery(q);
while (rs.next()) {
t = new Team();
t.setId(rs.getLong(1));
t.setName(rs.getString(2));
t.setManager(rs.getString(3));
t.setCoach(rs.getString(4));
t.setMood(rs.getString(5));
t.setPoints(rs.getInt(6));
}
} catch (SQLException ex) {
ex.printStackTrace();
}
close();
return t;
}
public Team getById(Long nm) {
open();
Team t = null;
q = "select id,name,manager,coach,mood,points from team where id = " + nm + " ;";
try {
ResultSet rs = s.executeQuery(q);
while (rs.next()) {
t = new Team();
t.setId(rs.getLong(1));
t.setName(rs.getString(2));
t.setManager(rs.getString(3));
t.setCoach(rs.getString(4));
t.setMood(rs.getString(5));
t.setPoints(rs.getInt(6));
}
} catch (SQLException ex) {
ex.printStackTrace();
}
close();
return t;
}
public Vector getAll() {
open();
Vector teams = new Vector();
q = "select id,name, manager,coach,mood,points from team";
try {
ResultSet rs = s.executeQuery(q);
while (rs.next()) {
Team t = new Team();
t.setId(rs.getLong(1));
t.setName(rs.getString(2));
t.setManager(rs.getString(3));
t.setCoach(rs.getString(4));
t.setMood(rs.getString(5));
t.setPoints(rs.getInt(6));
teams.add(t);
}
} catch (SQLException ex) {
ex.printStackTrace();
}
close();
return teams;
}
}
Fisierul de proprietati ‘DatabaseProps’ contine informatiile necesare pt conexiune la baza de date:
user=user
pass=user
url=jdbc:postgresql://localhost:5432/game
driver=org.postgresql.Driver
Clasa TeamValidator arata asa:
Eu am facut o validare pentru echipe, si am gandit asa: cand este selectata prima echipa se apeleaza acet validator si este setat id-ul acestei echipe, cand se seteaza a doua echipa se apeleaza din nou acest validator si se seteaza id-ul al doilea, apoi se verifica daca aceste id-uri sunt diferite, asta avertizeaza asupra faptului ca o echipa nu poate juca cu ea insesi.
package gui.validators;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import jdba.ScoreService;
import jsfp.Team;
public class TeamValidator implements Validator {
static long t1 = -1;
static long t2 = -1;
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
Team s = (Team) value;
if (t1 == -1) {
t1 = s.getId();
} else {
t2 = s.getId();
ScoreService ss = new ScoreService();
if (ss.gameDisputed(t1, t2)) {
t1 = t2 = -1;
FacesMessage message = new FacesMessage();
message.setDetail("Game already disputed");
message.setSummary("Game already disputed");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
if (t1 == t2) {
t1 = t2 = -1;
FacesMessage message = new FacesMessage();
message.setDetail("Teams must not be equal");
message.setSummary("Teams must not be equal");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
t1 = t2 = -1;
}
}
}
Clasa TeamGetAll arata asa:
package gui;
import java.util.Vector;
import javax.faces.model.SelectItem;
import jdba.TeamService;
import jsfp.Team;
public class TeamGetAll {
public Vector
getAll(){
Vector
sis = new Vector
();
TeamService ts = new TeamService();
Vector vt = ts.getAll();
for(Team t:vt){
SelectItem si = new SelectItem(t,t.getName());
sis.add(si);
}
return sis;
}
public Vector getTeams(){
TeamService ts = new TeamService();
return ts.getAll();
}
}
Clasa ScoresGetAll arata asa:
package gui;
import java.util.Vector;
import jdba.ScoreService;
import jsfp.Score;
public class ScoresGetAll {
public Vector getScores() {
ScoreService ts = new ScoreService();
return ts.getAll();
}
}
Daca inca mai citesti inseamna ca ai vointa
. Acum o sa pun si niste poze cu bucatelele importante din paginile jsp:
pagina de index arata asa:

Pagina View arata asa:

Pagina Teams arata asa:

Pagina Edit arata asa:

Cam asta e tot…
cireasa de pe tort: asta e o aplicatie mica :d analiza placuta si cititi cu atentie tutorialele de la linkurile pe care le-am pus la inceputul paginii.
PS: asta e si faces config:

Spor in continuare.