In functiile javascript, daca aveti nevoie de functionalitati JQuery, folositi functia jQuery(.....) in loc de $(...). In aceasta maniera orice conflict dintre jQuery si prototype este rezolvat.
luni, 24 august 2009
Richfaces and JQuery
Richfaces este un framework bazat pe JSF care ofera o gama larga de componente "ajax-enabled". Componentele din acest framework utilizeaza libraria javascript prototype. Din acest motiv trebuie sa aveti grija in momentul in care doriti sa folositi JQuery. In acest weekend, am avut nevoie sa folosesc componenta dataScroller din richfaces precum si galeria foto lightbox(plugin pentru jquery). Nu mica mi-a fost mirarea in momentul in care componenta dataScroller nu mergea asa cum era de asteptat(linkurile paginilor nu mergeau si apareau erori javascript). Dupa ce am cautat pe internet si am citit si in referinta oficiala am aflat ca trebuie sa importam jquery intr-un mod specific richfaces:
<a4j:loadScript src="resource://jquery.js" >
In functiile javascript, daca aveti nevoie de functionalitati JQuery, folositi functia jQuery(.....) in loc de $(...). In aceasta maniera orice conflict dintre jQuery si prototype este rezolvat.
In functiile javascript, daca aveti nevoie de functionalitati JQuery, folositi functia jQuery(.....) in loc de $(...). In aceasta maniera orice conflict dintre jQuery si prototype este rezolvat.
miercuri, 19 august 2009
Python SQLAlchemy ORM
Zilele trecute a trebuit sa implementez niste entitati care sa mapeze tabele dintr-o baza de date la o logica obiectuala. In Java, acest lucru il fac utilizand Hibernate. In Python am ales sa folosesc SQLAlchemy. La inceput sintaxa a fost un pic derutanta dar dupa ce m-am obisnuit cu ea a fost extrem de usor sa implementez clasele de care aveam nevoie. Ca backend am folosit o baza de date Oracle XE. In continuare voi arata cum se poate implementa o relatie one-to-many si cum se pot implementa query-uri utilizand SQLAlchemy. Pentru a face lucrurile un pic mai interesante voi utiliza o cheie primara compozita. Voi presupune ca implementam entitatile in pachetul da.
1. In fisierul __init__.py am ales sa instantiez dependentele sqlalchemy ce vor fi folosite in fiecare entitate.
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
BaseEntity = declarative_base()
strDbURL = "oracle://db_tes:xxx@xe"
objDbEngine = create_engine(strDbURL, echo=False)
Session = sessionmaker(bind=objDbEngine)
Session.configure(bind=objDbEngine)
def getConnection():
return Session()
In acest moment putem incepe sa implementam entitatile. Fiecare entitate va extinde BaseEntity. De fiecare data cand vrem sa obtinem o conexiune la baza de date utilizam metoda getConnection(). O sessiune SQLAlchemy este asemanatoare cu o sesiune Hibernate.
2. Definim o clasa utilizator care are ca si cheie primara(username si parola --- nu faceti asa in practica: puteti alege cnp-ul ca metoda de identificare). Aici vreau doar sa demonstrez utilizarea cheilor compozite.
from da import BaseEntity
from sqlalchemy import Column,Integer,String,Numeric,DateTime,ForeignKey,Sequence
from sqlalchemy.orm import relation, backref
class User(BaseEntity):
username = Column("username", String, primary_key=True)
password = Column("password", String, primary_key=True)
descriere = Column("descriere", String)
def __init__(self, strUsername, strPassword, strDesc):
self.username = strUsername
self.password = strPassword
self.descriere = strDesc
3. In continuare implementam clasa adresa.
from da import BaseEntity
from sqlalchemy import Column,Integer,String,Numeric,DateTime,ForeignKey,Sequence
from sqlalchemy.orm import relation, backref
class Adress(BaseEntity):
id = Column("id", Integer, Sequence("seq_xxx_id"), primary_key=True)
fk_username = Column("fk_username", String, ForeignKey("utilizatori.username"))
fk_password = Column("fk_password", String, ForeignKey("utilizatori.password"))
street = Column("street", String)
number = Column("number", String)
user = relation("User",
foreign_keys=[fk_username, fk_password],
primaryjoin="User.username == Address.fk_username and "
"User.password == Address.fk_password",
backref="adresses")
def __init__(self, strUser, strPasswd, strStreet, strNumber):
self.fk_username = strUser
self.fk_password = strPassword
self.street = strStreet
self.number = strNumber
Observatii: in acest exemplu incerc sa demonstrez puterea si flexibilitatea pe care o ofera SQLAlchemy. In primul rand, in orice entitate SQLAlchemy vom defini structura tabelei mapata la atribute ale entitatii. Utilizarea functiei relation permite implementarea legaturilor de tip one-to-one, one-to-many si many-to-many. De asemenea, utilizarea argumentului keyword backref indica frameworkului sa adauge automat un atribut la capatul celalalt al relatiei.
4. Dupa ce am implementat toate entitatile trebuie sa le si folosesc. In exemplul de mai jos arata o succesiune de linii de cod care demonstreaza functionalitatea entitatilor.
import da
from da import User, Address
# query pentru a selecta toti utilizatorii din bd
objSes = da.getConnection()
for objUsr, in objSes.query(User).all():
print(objUsr.addresses)
# query pentru a conditiona returnarea unui utilizator care are o anumita adresa
for objUsr in objSes.query(User, Address).filter(User.username == Address.fk_username and User.password == Address.fk_password).filter(Address.id == 1).all():
print(objUsr.username)
In concluzie, SQLAlchemy faciliteaza maparea tabelelor in logica obiect(la fel ca orice ORM) dar nu necesita un fisier de configurare separat sau anotari suplimentare(precum Hibernate). De asemenea, scrierea de query-uri este extrem de usoara. Ceea ce nu s-a aratat dar este intuitiv este adaugare de noi entitati in bd. Pentru aceasta folositi objSes.add(objInstance). Folositi add chiar daca vreti sa faceti update(SQLAlchemy va sti ce doriti sa faceti in functie de atributele setate in instanta).
1. In fisierul __init__.py am ales sa instantiez dependentele sqlalchemy ce vor fi folosite in fiecare entitate.
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
BaseEntity = declarative_base()
strDbURL = "oracle://db_tes:xxx@xe"
objDbEngine = create_engine(strDbURL, echo=False)
Session = sessionmaker(bind=objDbEngine)
Session.configure(bind=objDbEngine)
def getConnection():
return Session()
In acest moment putem incepe sa implementam entitatile. Fiecare entitate va extinde BaseEntity. De fiecare data cand vrem sa obtinem o conexiune la baza de date utilizam metoda getConnection(). O sessiune SQLAlchemy este asemanatoare cu o sesiune Hibernate.
2. Definim o clasa utilizator care are ca si cheie primara(username si parola --- nu faceti asa in practica: puteti alege cnp-ul ca metoda de identificare). Aici vreau doar sa demonstrez utilizarea cheilor compozite.
from da import BaseEntity
from sqlalchemy import Column,Integer,String,Numeric,DateTime,ForeignKey,Sequence
from sqlalchemy.orm import relation, backref
class User(BaseEntity):
username = Column("username", String, primary_key=True)
password = Column("password", String, primary_key=True)
descriere = Column("descriere", String)
def __init__(self, strUsername, strPassword, strDesc):
self.username = strUsername
self.password = strPassword
self.descriere = strDesc
3. In continuare implementam clasa adresa.
from da import BaseEntity
from sqlalchemy import Column,Integer,String,Numeric,DateTime,ForeignKey,Sequence
from sqlalchemy.orm import relation, backref
class Adress(BaseEntity):
id = Column("id", Integer, Sequence("seq_xxx_id"), primary_key=True)
fk_username = Column("fk_username", String, ForeignKey("utilizatori.username"))
fk_password = Column("fk_password", String, ForeignKey("utilizatori.password"))
street = Column("street", String)
number = Column("number", String)
user = relation("User",
foreign_keys=[fk_username, fk_password],
primaryjoin="User.username == Address.fk_username and "
"User.password == Address.fk_password",
backref="adresses")
def __init__(self, strUser, strPasswd, strStreet, strNumber):
self.fk_username = strUser
self.fk_password = strPassword
self.street = strStreet
self.number = strNumber
Observatii: in acest exemplu incerc sa demonstrez puterea si flexibilitatea pe care o ofera SQLAlchemy. In primul rand, in orice entitate SQLAlchemy vom defini structura tabelei mapata la atribute ale entitatii. Utilizarea functiei relation permite implementarea legaturilor de tip one-to-one, one-to-many si many-to-many. De asemenea, utilizarea argumentului keyword backref indica frameworkului sa adauge automat un atribut la capatul celalalt al relatiei.
4. Dupa ce am implementat toate entitatile trebuie sa le si folosesc. In exemplul de mai jos arata o succesiune de linii de cod care demonstreaza functionalitatea entitatilor.
import da
from da import User, Address
# query pentru a selecta toti utilizatorii din bd
objSes = da.getConnection()
for objUsr, in objSes.query(User).all():
print(objUsr.addresses)
# query pentru a conditiona returnarea unui utilizator care are o anumita adresa
for objUsr in objSes.query(User, Address).filter(User.username == Address.fk_username and User.password == Address.fk_password).filter(Address.id == 1).all():
print(objUsr.username)
In concluzie, SQLAlchemy faciliteaza maparea tabelelor in logica obiect(la fel ca orice ORM) dar nu necesita un fisier de configurare separat sau anotari suplimentare(precum Hibernate). De asemenea, scrierea de query-uri este extrem de usoara. Ceea ce nu s-a aratat dar este intuitiv este adaugare de noi entitati in bd. Pentru aceasta folositi objSes.add(objInstance). Folositi add chiar daca vreti sa faceti update(SQLAlchemy va sti ce doriti sa faceti in functie de atributele setate in instanta).
vineri, 7 august 2009
Java si mocking - PowerMock
In unele posturi precedente am vorbit despre cum puteti testa cod python prin eliminarea dependentelor. Acelasi lucru il puteti face si in java desi este un pic mai complicat. Dupa ce am studiat mai multe librarii pentru mock am ajuns la concluzia ca PowerMock se apropie cel mai mult de libraria mocker pentru python. Aceasta librarie aduce imbunatatiri pentru EasyMock iar in ultimele versiuni vine cu suport si pentru Mockito. Printre principalele functionalitati ale PowerMock mentionez:
class Calculator {
public static int add(int a1, int a2) {
return a1 + a2;
}
}
class Operatii {
public int operatieComplexa(int a1, a2) {
int a = Calculator.add(a1, a2);
return ++a;
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({Calculator.class})
class TestOperatii {
@Test
public void testOperatieComplexa() {
PowerMock.mockStatic(Calculator.class);
EasyMock.expect(Calculator.add(1, 2).andReturn(3);
PowerMock.replayAll();
Operatii obj = new Operatii();
int ret = obj.operatieComplexa(1, 2);
PowerMock.verifyAll();
Assert.assertEquals(4, ret);
}
}
Asta e tot. In momentul in care se executa testul in loc sa se foloseasca metoda statica add din clasa Calculator se foloseste o metoda injectata. Cateva elemente interesante sunt enumerate mai jos:
- mock pentru functii statice
- mock pentru functii private
class Calculator {
public static int add(int a1, int a2) {
return a1 + a2;
}
}
class Operatii {
public int operatieComplexa(int a1, a2) {
int a = Calculator.add(a1, a2);
return ++a;
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({Calculator.class})
class TestOperatii {
@Test
public void testOperatieComplexa() {
PowerMock.mockStatic(Calculator.class);
EasyMock.expect(Calculator.add(1, 2).andReturn(3);
PowerMock.replayAll();
Operatii obj = new Operatii();
int ret = obj.operatieComplexa(1, 2);
PowerMock.verifyAll();
Assert.assertEquals(4, ret);
}
}
Asta e tot. In momentul in care se executa testul in loc sa se foloseasca metoda statica add din clasa Calculator se foloseste o metoda injectata. Cateva elemente interesante sunt enumerate mai jos:
- @RunWith(PowerMockRunner.class). Indica JUnit sa foloseasca clasa PowerMockRunner pentru rularea testelor(nu merge fara asta datorita faptului ca PowerMock implementeaza un class loader custom).
- @PrepareForTest({Calculator.class}). In aceasta anotare se enumera clasele pentru care se va face mockStatic.
miercuri, 29 iulie 2009
Python si servicii web
Recent a trebuit sa apelez din Python un serviciu web. In momentul de fata, "consumarea" serviciilor web din Python este greoaie. O solutie relativ usor de implementat este aceea de construire a unei cereri SOAP. In exemplul urmator voi arata cum poate fi invocata o metoda a serviciului web de pe site-ul infovalutar.ro.
------------------------------------------------------------------------------------------------
import httplib
HOST = "www.infovalutar.ro"
URL = "/curs.asmx"
def SOAP_GetMoneda(strMoneda):
strReq = """<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<getlatestvalue xmlns="http://www.infovalutar.ro/">
<Moneda>%s</Moneda>
</getlatestvalue>
</soap12:Body>
</soap12:Envelope>"""
return strReq % strMoneda
if __name__ == "__main__":
data = SOAP_GetMoneda("USD")
dctHeaders = {"Host" : HOST,
"Content-Type" : "text/xml; charset=utf-8",
"Content-Length" : len(data),
"SOAPAction" : "http://www.infovalutar.ro/getlatestvalue"}
con = httplib.HTTPConnection(HOST)
con.request("POST", URL, data, dctHeaders)
resp = con.getresponse()
print(resp.read())
con.close()
------------------------------------------------------------------------------------------------
Ceea ce nu se arata in acest exemplu este modul in care se parseaza raspunsul. Apelul resp.read() returneaza un string care reprezinta un document xml. Parsarea raspunsului nu este prezentata aici intrucat nu acesta este scopul articolului. Acelasi rezultat poate fi obtinut si prin utilizarea bibliotecilor urllib si urllib2, dar in momentul de fata httplib este libraria recomandata.
------------------------------------------------------------------------------------------------
import httplib
HOST = "www.infovalutar.ro"
URL = "/curs.asmx"
def SOAP_GetMoneda(strMoneda):
strReq = """<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<getlatestvalue xmlns="http://www.infovalutar.ro/">
<Moneda>%s</Moneda>
</getlatestvalue>
</soap12:Body>
</soap12:Envelope>"""
return strReq % strMoneda
if __name__ == "__main__":
data = SOAP_GetMoneda("USD")
dctHeaders = {"Host" : HOST,
"Content-Type" : "text/xml; charset=utf-8",
"Content-Length" : len(data),
"SOAPAction" : "http://www.infovalutar.ro/getlatestvalue"}
con = httplib.HTTPConnection(HOST)
con.request("POST", URL, data, dctHeaders)
resp = con.getresponse()
print(resp.read())
con.close()
------------------------------------------------------------------------------------------------
Ceea ce nu se arata in acest exemplu este modul in care se parseaza raspunsul. Apelul resp.read() returneaza un string care reprezinta un document xml. Parsarea raspunsului nu este prezentata aici intrucat nu acesta este scopul articolului. Acelasi rezultat poate fi obtinut si prin utilizarea bibliotecilor urllib si urllib2, dar in momentul de fata httplib este libraria recomandata.
luni, 27 iulie 2009
JSF si localizarea
Localizarea este procesul prin care o aplicatie(web sau desktop) poate prezenta continutul in mai multe limbi. Este un subiect de actualitate si nu exista inca un standard despre cum ar trebui implementat. In Java/JSF se poate folosi clasa ResourceBundle din pachetul java.util. Pasii care trebuie urmati sunt prezentati printr-un exemplu.
1. Se creeaza doua fisiere de resurse(limba engleza si limba romana)
In fiecare dintre ele se adauga o cheie lbWelcome=........ cu textul in engleza, respectiv romana.
2. se modifica fisierul faces-config.xml pentru a indica limbile suportate pentru localizare.
<application>
<locale-config>
<default-locale>ro< /default-locale>
<supported-locale>ro< /supported-locale>
<supported-locale>en< /supported-locale>
< / locale-config>
< /application>
3. Se creeaza o pagina jsp pentru pentru prezentarea continutului.
<%@page pageEncoding="UTF-8" contentType="text/html; charset=ISO-8859-2" %>
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<f:view>
< h:form>
<h:commandLink action="#{languageBean.changeLanguage}" value="Romana">
<f:param name="lang" value="ro">
< /h:commandLink>
|
<h:commandLink action="#{languageBean.changeLanguage}" value="English">
<f:param name="lang" value="en">
< /h:commandLink><>
<h:outputText value="#{msg.lbWelcome}">
</h:form>
</f:view>
4. Se creeaza un backbean in care implementez metoda de schimbare limba.
package ro.testlocalization.traduceri;
import java.util.Locale;
import java.util.Map;
import javax.faces.application.Application;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
public class LanguageBean {
public void changeLanguage() {
Map req = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String lang = req.get("lang").toString();
Locale newLocale = new Locale(lang);
FacesContext context = FacesContext.getCurrentInstance();
context.getViewRoot().setLocale(newLocale);
}
}
5. Se mapeaza backbean-ul in faces-config.xml
6. Asta e tot. In acest moment ar trebui sa puteti rula aplicatia si sa puteti schimba limba de afisare a mesajului.
1. Se creeaza doua fisiere de resurse(limba engleza si limba romana)
In fiecare dintre ele se adauga o cheie lbWelcome=........ cu textul in engleza, respectiv romana.
2. se modifica fisierul faces-config.xml pentru a indica limbile suportate pentru localizare.
<application>
<locale-config>
<default-locale>ro< /default-locale>
<supported-locale>ro< /supported-locale>
<supported-locale>en< /supported-locale>
< / locale-config>
< /application>
3. Se creeaza o pagina jsp pentru pentru prezentarea continutului.
<%@page pageEncoding="UTF-8" contentType="text/html; charset=ISO-8859-2" %>
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<f:view>
<
<h:commandLink action="#{languageBean.changeLanguage}" value="Romana">
<f:param name="lang" value="ro">
< /h:commandLink>
|
<h:commandLink action="#{languageBean.changeLanguage}" value="English">
<f:param name="lang" value="en">
< /h:commandLink><>
<h:outputText value="#{msg.lbWelcome}">
</h:form>
</f:view>
4. Se creeaza un backbean in care implementez metoda de schimbare limba.
package ro.testlocalization.traduceri;
import java.util.Locale;
import java.util.Map;
import javax.faces.application.Application;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
public class LanguageBean {
public void changeLanguage() {
Map req = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
String lang = req.get("lang").toString();
Locale newLocale = new Locale(lang);
FacesContext context = FacesContext.getCurrentInstance();
context.getViewRoot().setLocale(newLocale);
}
}
5. Se mapeaza backbean-ul in faces-config.xml
6. Asta e tot. In acest moment ar trebui sa puteti rula aplicatia si sa puteti schimba limba de afisare a mesajului.
marți, 30 iunie 2009
Servicii web cu jax-ws: Partea 2
Acest post vine ca o continuare pentru prima parte despre serviciile web cu jax-ws. O problema care apare des in serviciile web este aceea a datelor ce pot fi returnate. Un exemplu posibil este urmatorul: Am un EJB care modeleaza o persoana. Cum pot sa il returnez? O abordare naiva ar putea fi aceea de a specifica return type-ul ca fiind clasa EJB-ului. De ce nu este corecta aceasta abodare? In momentul in care clientul primeste obiectul si incearca de exemplu sa acceseze un element dintr-o colectie de tip OneToMany nu va putea face acest lucru(session closed). Ce putem face? Putem scrie un obiect wrapper care sa fie serializabil. In metoda serviciului web construim obiectul wrapper pe baza ejb-ului incarcat. Avem grija sa nu folosim tipuri de colectii de genul List, Set, etc... Vom folosi in schimb vectori normali []. Codul de mai jos exemplifca acest lucru.
@Entity
public class Persoana {
@Id
private long cnp;
@OneToMany(mappedBy="parinte")
private List rude;
//metode getter/setter
}
public class PersoanaWrapper implements Serializable {
private long cnp;
private PersoanaWrapper[] rude;
//metode getter/setter
}
Metoda din webservice care doreste sa returneze o persoana ar putea fi implementat de maniera urmatoare:
public PersoanaWrapper getPersoana(long cnp) {
//cod prin care incarc ejb-ul persoanei dorite. => Persoana persEJB;
PersoanaWrapper pers = new PersoanaWrapper();
int nrRude = persEJB.getRude().size();
pers.setCNP(persEJB.getCNP());
pers.setRude(new PersoanaWrapper[nrRude]);
for(int i = 0; i < nrRude; i++) {
PersoanaWrapper tmp = new PersoanaWrapper();
tmp.setCNP(persEJB.getRude().get(i).getCNP());
pers.getRude()[i] = tmp;
}
return pers;
}
O imbunatatire vizibila a codului de mai sus este reprezentata de adaugarea unei metode/constructor de copiere: "valueOf". Utilizand clase wrapper, puteti sa returnati orice tip de date doriti.
S-a mentionat intr-un post anterior ca in momentul in care un serviciu web este deployat in container el va fi expus si ca Session bean. Este recomandat totusi sa accesati serviciul ori ca session bean ori ca web service(de preferat web service) pentru a fi sigur ca toate metodele sunt functionale.
Cum pot injecta resursa in serviciul web?
De cele mai multe ori este nevoie sa interactionam intr-un fel cu un sistem backend(o baza de date, un fisier txt, etc..) Pentru acest lucru, in Java EE exista PersistenceContext. Contextul de persistenta poate fi injectat usor utilizand urmatoarele linii de cod:
@PersistenContext(unitName="myPersistence")
private EntityManager em;
Contextul de persistenta nu este singura resursa care poate fi injectata intr-un serviciu web. Alte resurse pot fi de asemenea injectat. De exemplu un mail session sau un datasource(pentru bd). Presupun ca avem un DataSource mapat ca: java:jdbc/MyDS. Pentru a-l face disponibil in cod este suficient sa folosesc urmatoarea constructie:
@Resource(mappedName="java:jdbc/MyDS")
private DataSource ds;
Se procedeaza analog pentru orice fel de resursa doriti sa injectati. Intr-un post urmator voi arata cateva strategii pentru securizarea unui serviciu web.
@Entity
public class Persoana {
@Id
private long cnp;
@OneToMany(mappedBy="parinte")
private List
//metode getter/setter
}
public class PersoanaWrapper implements Serializable {
private long cnp;
private PersoanaWrapper[] rude;
//metode getter/setter
}
Metoda din webservice care doreste sa returneze o persoana ar putea fi implementat de maniera urmatoare:
public PersoanaWrapper getPersoana(long cnp) {
//cod prin care incarc ejb-ul persoanei dorite. => Persoana persEJB;
PersoanaWrapper pers = new PersoanaWrapper();
int nrRude = persEJB.getRude().size();
pers.setCNP(persEJB.getCNP());
pers.setRude(new PersoanaWrapper[nrRude]);
for(int i = 0; i < nrRude; i++) {
PersoanaWrapper tmp = new PersoanaWrapper();
tmp.setCNP(persEJB.getRude().get(i).getCNP());
pers.getRude()[i] = tmp;
}
return pers;
}
O imbunatatire vizibila a codului de mai sus este reprezentata de adaugarea unei metode/constructor de copiere: "valueOf". Utilizand clase wrapper, puteti sa returnati orice tip de date doriti.
S-a mentionat intr-un post anterior ca in momentul in care un serviciu web este deployat in container el va fi expus si ca Session bean. Este recomandat totusi sa accesati serviciul ori ca session bean ori ca web service(de preferat web service) pentru a fi sigur ca toate metodele sunt functionale.
Cum pot injecta resursa in serviciul web?
De cele mai multe ori este nevoie sa interactionam intr-un fel cu un sistem backend(o baza de date, un fisier txt, etc..) Pentru acest lucru, in Java EE exista PersistenceContext. Contextul de persistenta poate fi injectat usor utilizand urmatoarele linii de cod:
@PersistenContext(unitName="myPersistence")
private EntityManager em;
Contextul de persistenta nu este singura resursa care poate fi injectata intr-un serviciu web. Alte resurse pot fi de asemenea injectat. De exemplu un mail session sau un datasource(pentru bd). Presupun ca avem un DataSource mapat ca: java:jdbc/MyDS. Pentru a-l face disponibil in cod este suficient sa folosesc urmatoarea constructie:
@Resource(mappedName="java:jdbc/MyDS")
private DataSource ds;
Se procedeaza analog pentru orice fel de resursa doriti sa injectati. Intr-un post urmator voi arata cateva strategii pentru securizarea unui serviciu web.
luni, 29 iunie 2009
Servicii web cu jax-ws: Partea 1
In unul din proiectele recente in care am fost implicat am avut nevoie sa implementez o arhitectura SOA(service oriented application). Partea de business era expusa prin intermediul serviciilor web. Aceasta solutie a luat nastere datorita faptului ca una din componente era scrisa in C# in timp ce restul componentelor utilizau JSF/Java EE. Container-ul ales a fost JBoss 5 pentru stratus entreprise(servicii web, ejb) si Tomcat 6 pentru front-end(aplicatia web). Prima problema majora a fost cauza de versiune de JBoss 5 downloadata.
Obs: !!!!!!!!!Daca utilizati jre/jdk 6 downloadati versiune de jboss compilata pentru java 6. Astfel nu veti putea accesa serviciile web, in mare parte datorita unui conflict din saaj.jar din jboss si saaj.jar din jdk 6.
Acum vom presupune ca aveti versiunea corecta de jboss pe calculatorul dumneavoastra. Java EE/jax-ws usureaza foarte mult dezvoltarea de servicii web.
@WebService
public interface ServiciuTest {
@WebMethod
public String sayHello(@WebParameter(name="message")String msg);
}
In continuare voi arata cum se poate implementa un serviciu web care transmite un fisier.
@WebService
public interface ServiciuFilesTest {
@WebMethod
public byte[] getFile(@WebParam(name="file_name")String fName);
}
}
}
Asta e tot. In acest fel putem trimite fisiere de la webservice catre un client. De obicei este recomandat sa encodam continutul trimis in Base64 pentru a garanta faptul ca nu vor aparea probleme.
Scenariul in care am folosit abordarea de mai sus a fost urmatorul: clientul web face o cerere pentru un document(dupa id ci nu dupa cale absoluta). Serviciul web interogheaza baza de date, citeste continutul si il returneaza catre clientul web. Acesta salveaza continutul local si il afiseaza in interfata.
Intr-un post ulterior voi arata cum putem returna date complexe printr-un serviciu web si cum putem folosi injectarea resurselor(specific pentru Java EE).
Obs: !!!!!!!!!Daca utilizati jre/jdk 6 downloadati versiune de jboss compilata pentru java 6. Astfel nu veti putea accesa serviciile web, in mare parte datorita unui conflict din saaj.jar din jboss si saaj.jar din jdk 6.
Acum vom presupune ca aveti versiunea corecta de jboss pe calculatorul dumneavoastra. Java EE/jax-ws usureaza foarte mult dezvoltarea de servicii web.
@WebService
@SOAPBinding(style=Style.RPC)@Stateless
public interface ServiciuTest {
@WebMethod
public String sayHello(@WebParameter(name="message")String msg);
}
@StatelessAsta e tot ce trebuie sa faceti pentru a avea un serviciu web functional. In acest moment serviciul poate fi deployat pe un container care suporta jax-ws. In Java EE serviciul de mai sus va fi expus atat ca serviciu web cat si ca un session bean. Interfata reprezinta de fapt o specificatie pentru serviciul web(endpoint). Retineti faptul ca nu veti putea returna obiecte care nu sunt serializabile.
@WebService(endpointInterface="ServiciuTest")
public class ServiciuTestBean {
public String sayHello(String msg) {
return msg;
}
}
In continuare voi arata cum se poate implementa un serviciu web care transmite un fisier.
@WebService
@SOAPBinding(style=Style.RPC)@Stateless
public interface ServiciuFilesTest {
@WebMethod
public byte[] getFile(@WebParam(name="file_name")String fName);
}
@Statelesspublic byte[] getFile(String fName) {
@WebService(endpointInterface="ServiciuFilesTest")
public class ServiciuFilesTestBean {
byte[] deRet = new byte[(int)(new File(fName)).length()];return deRet;
try {
FileInputStream file = new FileInputStream(fName);
file.read(deRet);
file.close();
}
catch(IOException ioe) {
ioe.printStackTrace();
return null;
}
}
}
Asta e tot. In acest fel putem trimite fisiere de la webservice catre un client. De obicei este recomandat sa encodam continutul trimis in Base64 pentru a garanta faptul ca nu vor aparea probleme.
Scenariul in care am folosit abordarea de mai sus a fost urmatorul: clientul web face o cerere pentru un document(dupa id ci nu dupa cale absoluta). Serviciul web interogheaza baza de date, citeste continutul si il returneaza catre clientul web. Acesta salveaza continutul local si il afiseaza in interfata.
Intr-un post ulterior voi arata cum putem returna date complexe printr-un serviciu web si cum putem folosi injectarea resurselor(specific pentru Java EE).
Abonați-vă la:
Postări (Atom)
