luni, 15 decembrie 2008

BIRT si Java

In continuare voi prezenta un subiect care mi s-a parut extrem de interesant. Mediu de raportare in lumea dezvoltatorilor Java/Java EE. O varianta ar fi sa achizitionati o licenta de developer pentru Crystal Reports. Desi suna foarte bine nu as recomanda aceasta varianta. Am lucrat cu modulul de Crystal Reports pentru Eclipse, dar rezultatele au fost dezamagitoare. In primul rand se vede clar ca plugin-ul nu este complet. Nu am reusit sa creez o sursa de date pe baza unei proceduri stocate utilizand Oracle 10g2. Dupa mai multe incercari am renuntat la ideea de Crystal Reports si am incercat sa gasesc o alternativa. O alternativa buna si perfect adaptata la eclipse este BIRT. Este extrem de usor de folosit si se obtin performante bune. Lucreaza in aceeasi maniera ca si Crystal Reports, nu are costuri de licentiere si dispune de un viewer ce poate fi integrat in aplicatii.

Metoda recomandata pentru folosirea sa ar fi sa se creeze un context doar pentru runtime numit /birt-viewer. Pasii de instalare pentru viewer se gasesc pe http://www.eclipse.org/. Intr-un post urmator voi arata cum se poate crea un proiect jsp care sa acceseze rapoarte. In continuare voi prezenta elementele specifice birt.

Principalele elemente ale birt-ului sunt:
  • liste
  • tabele
  • griduri
  • labels
  • dynamic text
  • grafice / charturi
  • surse de date
  • imagini
  • triggere

De asemenea, rapoartele din birt pot refolosi librarii de elemente precum si master-pages. Astfel, se poate obtine un aspect unitar pentru mai multe rapoarte din proiect.

O alta parte utila oferita de birt este viewer-ul ajax. Acesta poate fi integrat usor intr-o pagina jsp si poate sa aiba parametrii binduiti la anumite valori de pe request/session/application. Intr-un post urmator voi prezenta o mica aplicatie jsf in care se folosesc si rapoarte.

Sper ca acest post sa fie util pentru dumneavoastra.

Cosnita Radu Viorel!

marți, 18 noiembrie 2008

Oracle si Proceduri Stocate Java

Incepand cu versiunea Oracle 8i serverul de baze de date contine o masina virtuala nativa. Acest lucrul inseamna ca se pot stoca proceduri/programe scrise in java direct in baza de date. Acest lucru este util daca doriti sa adaugati functionalitati noi serverului. In cele ce urmeaza vom crea o clasa simpla care va contine o metoda cu un parametru de tip java.lang.String. Aceasta metoda va reprezenta de fapt procedura stocata.

Presupun ca se foloseste ca mediu de dezvoltare eclipse. Creati un proiect de tip "Java project". Adaugati ca referinta ojdbc14.jar(driver-ul jdbc pentru Oracle). In acest moment suntem gata sa adaugam clasa dorita.

package ro.teste.sp;

public class TesteSP
{
public static void WriteMessage(String msg) throws SQLException
{
Connection con = DriverManager.getConnection("jdbc:default:connection");

System.out.println("S-a transmis din PL/SQL: " + msg);
}
}


Pasul 2: se incarca clasa in baza de date

loadjava -user user/passwd@db TesteSP.java

Pasul 3: se creaza un wrapper in baza de date.

CREATE OR REPLACE PROCEDURE WrapJava1(msg VARCHAR2)
AS
LANGUAGE java
NAME 'ro.teste.sp.TesteSP.WriteMessage(java.lang.String)';

Pasul 4: executam procedura creata

CALL WrapJava1('Salutari');

Acest exemplu este unul extrem de simplu. Puteti crea exemple mai complicate. De exemplu, poate dorit sa generati o imagine pe baza unui anumite inregistrari. Sper ca veti gasi multe aplicatii pentru aceasta facilitate oferita de Oracle.

Observatii: Ar trebui sa observati un mod ciudat de obtinere a unei conexiuni la baza de date. Desi in acest exemplu, conexiune obtinuta nu este folosita este de retinut acest mod de conectare. Intrucat, metoda ruleaza in interiorul bazei de date, jdbc:default:connection ofera conexiunea curenta metodei.

Daca va placut acest articol sau daca il gasiti util nu ezitati sa adaugati un comentariu.

Cosnita Radu Viorel!

vineri, 7 noiembrie 2008

Oracle si JavaBean

In continuare voi arata cum se poate integra un JavaBean in Oracle Forms. Principala problema este ca cei de la Oracle nu ofera documentatie pentru frameworkul dezvoltat, asa ca ce ramane de facut este sa faceti un search pe google si sa studiati cat mai multe posturi. In continuare voi implementa un simplu JavaBean care va furniza un text catre Oracle Forms.

Pasul 1 este sa adaugati o referinta catre frmall.jar care se gaseste in locul in care ati instala Oracle Developer Suite/forms/java.

Pasul 2 este sa compilati urmatorul cod:

package ro.javabeans;

import oracle.forms.handler.IHandler;
import oracle.forms.properties.ID;
import oracle.forms.ui.CustomEvent;
import oracle.forms.ui.VBean;

public class HelloWorld extends VBean
{
private static IHandler myHandler;
protected static final ID MESAJ = ID.registerProperty("MESAJ");
private String msg;
public void setMsg(String s)
{
this.msg = s;
}
public String getMsg()
{
return this.msg;
}

public void init(IHandler handler)
{
myHandler = handler;
super.init(handler);
}

public boolean setProperty(ID property, Object value)
{
if(property == MESAJ)
{
this.setMsg((String)value);
return true;
}

return super.setProperty(property, value);
}

public Object getProperty(ID property)
{
if(property == MESAJ)
return this.getMsg();

return super.getProperty(property);
}

public void dispatchEvent(ID id)
{
CustomEvent ce = new CustomEvent(myHandler, id);
dispatchCustomEvent(ce);
}
}

Pasul 3: Consider ca ar trebui sa explic de ce se face asa. In primul rand, handler-ul pe care il setam ofera acces la diverse elemente din mediul forms.
protected static final ID MESAJ = ID.registerProperty("MESAJ") este importanta intrucat inregistreaza atribute ale bean-ului care vor putea fi referite in forms. Textul dintre ghilimele este case sensitive, asa ca recomand folosirea de majuscule.
setProperty si getProperty sunt foarte asemenatoare cu functiile getter/setter clasice. Ele sunt folosite in momentul in care se seteaza valoarea unui atribut din forms sau se cere valoarea unui atribut din forms.
Metoda dispatchEvent este folosita pentru a declansa evenimente ce pot fi captate in Oracle Forms.

Pasul 4: Exportarea proiectului creat mai sus intr-un jar.

Pasul 5: Trebuie modificat in fisierul DevsuiteHome/forms/server/formsweb.cfg valoarea pentru archive=....., nume.jar

Pasul 6: Se creeaza un formular si se adauga un item de tip JavaBean. La implementation class se seteaza: ro.javabeans.HelloWorld.

Pasul 7: Se adauga un trigger: when-new-form-instance care contine urmatorul cod:
declare
msg VARCHAR2(200) := 'Va pup pe toti';
begin
SET_CUSTOM_PROPERTY('BLOCK3.BEAN4', 1, 'MESAJ', 'SALUTARI');
end;

Observatii: Am presupus ca bean-ul se afla in data-block-ul BLOCK3 si se numeste BEAN4.
SET_CUSTOM_PROPERTY este un built-in folosit pentru a seta un atribut al bean-ului.

Pasul 8: Se adauga un item de tip buton.
Pasul 9: Se adauga un trigger pentru acest button de tip: when-mouse-click, cu urmatorul cod:
declare
msg VARCHAR2(2000) := GET_CUSTOM_PROPERTY('BLOCK3.BEAN4', 1, 'MESAJ');
begin
MESSAGE(msg);
end;

Observatii: GET_CUSTOM_PROPERTY este un builtin folosit pentru a returna valoarea atributului specificat.

Pasul 10:Rularea propriu-zisa a aplicatie.

Se observa ca integrarea dintre forms si java este destul de greoaie dar uneori merita intrucat se pot face lucruri complicate care sa adauge functionalitati noi la Oracle Forms.

Exemple de astfel de aplicatii: Transferul de tip FTP, Transmiterea de email-uri(se poate face si fara Java), generarea de imagini, etc..... Intr-un post viitor voi arata cum se pot imbina date dintr-un Oracle Forms cu un bean java pentru a genera imagini.

joi, 30 octombrie 2008

Oracle si functii ce returneaza mai multe inregistrari

Aceasta ideea mi-a venit intrucat am folosit destul de mult serverul de baze de date opensource PostgreSQL. In Postgres exista posibilitatea ca o functie sa apara in clauza from a unui select, fapt ce are mari avantaje. Acest lucru poate fi realizat si in oracle. Daca va intrebati de ce am avea nevoie de asa ceva, exemplul de mai jos ar trebui sa va lamureasca.

EX: Presupun ca folosesc un sistem de BI(Business Intelligence) pentru a realiza anumite rapoarte. Birt, Oracle Reports, Crystal Reports, etc. Este cunoscut faptul ca exista probleme de integrare intre Crystal Report in Eclipse si proceduri stocate ce returneaza cursoare.

Intrucat pot folosi functii in clauza from a unui select nu am decat sa scriu ca model al datelor pentru raport un query in loc de o procedura stocata. In realitate voi folosi tot functia stocata astfel incat decuplez logica din baza de date de partea de client.

Rezolvarea pentru problema descrisa consta in rezolvarea functiilor pipelined. Vom lua un caz simplu in care presupun ca am o tabela persoane(cnp, nume, prenume) si as vrea o functie care sa returneze toate inregistrarile.

Pasul 1:
Declar un tip persoana:
create or replace type tp_Persoana is object(CNP NUMBER,
Nume VARCHAR2(200),
Prenume VARCHAR2(200));


Pasul 2:
Declar un tip tabel de tp_Persoana.

create or replace type tp_TblPersoana is table of tp_persoana index by binary_integer;

Pasul 3:
Creez functia stocata:

create or replace function fc_TestPersoane return tp_tblpersoana pipelined
as
cursor c is select * from persoane;
rec tp_Persoana;
begin
OPEN c;

rec := tp_Persoana(null, null, null);

LOOP
FETCH c INTO rec.cnp, rec.nume, rec.prenume;
exit when c%notfound;

pipe row(rec);
END LOOP;

CLOSE c;
return;
end fc_TestPersoane;


Pasul 4:

In fine ultimul pas este apelul propriu-zis.

SELECT * FROM table(fc_TestPersoane());

Cateva observatii:

Aceasta facilitate este extrem de utila si de flexibila. Totusi trebuie sa o folositi doar in cazuri in care nu aveti alta solutie intrucat se obtine o utilizare suplimentara a memorie prin deschiderea cursorului si parcurgerea lui. Sper ca acest post sa fie util.

Cosnita Radu Viorel!

marți, 28 octombrie 2008

Hibernate si chei composite

Hibernate este un ORM extrem de utilizat in zilele noastre. In ultimele versiuni a aparut si Hibernate Annotations. In aceasta maniera se reuseste o uniformizare a framework-ului si o apropiere de JPA. Se foloseste Hibernate in Java pentru a mapa entitati(obiecte) la tabele din baza de date. In continuare prezint o modalitatea de a manipula o relatie many-to-many.


Problema intalnita este enuntata in cele ce urmeaza: am o relatie many-to-many intre un pachet de gazduire si tehnologie. Entitatea TehnologiiPachet, rezultata este implementata prin hibernate. Este evident faptul ca pentru aceasta entitate, cheia primara va fi: (pachet, tehnologie) => intr-un pachet nu pot sa am aceeasi tehnologie de mai multe ori. In continuare postez codul pentru aceste entitati precum si pentru cheia primara:

@Entity
@Table(name="tblpachete")
public class PachetGazduire
{
@Id
private int id;

/* aici vin implementate alte proprietati si metodele getter/setter */
@OneToMany(fetch=FetchType.EAGER, mappedBy="id.pachet")
private ArrayList tehnologii;
}


@Entity
@Table(name="tbltehnologii")
public class Tehnologie
{
@Id
private int id;

/* aici vin implementate alte proprietati si metodele getter/setter */
}



/* aici implementez cheia primara compozita */
@Embeddable
public class TehnologiiPachetPK
{
@ManyToOne
@JoinColumn(name="fk_pachet", referencedColumnName="id")
private PachetGazduire pachet;


@ManyToOne
@JoinColumn(name="fk_tehnologie", referencedColumnName="id")
private Tehnologie tehnologie;

/* aici vin implementate alte proprietati si metodele getter/setter */
}



/* aici vine entitatea de legatura */
@Entity
@Table(name="tbltehnologiipachet")
public class TehnologiiPachet
{
@Id
private TehnologiiPachetPK id;

int laCerere;
int nrConturi;
int pretLaCerere;

/* aici vin implementate alte proprietati si metodele getter/setter */
}


Acesta este codul care poate fi testat. In principiu, tabelele care se ascund in spatele acestor entitati pot fi generate relativ usor. O explicatie ce cred ca este binevenita: fetch=FetchType.EAGER. In mod implicit, hibernate face instantiere lazy. Asta inseamna ca vectorul de TehnologiiPachet ar fi incarcat doar daca incerc sa apelez un element din el. Acest caz presupune ca sesiune de hibernate ramane deschisa. Intr-o aplicatie de desktop acest lucru functioneaza, dar pentru o aplicatie de web nu mai este viabil. Din acest motiv folosesc o incarcare EAGER. Asta imi permite ca la orice moment ulterior incarcarii din baza de date accessare unei tehnologii din vector.

Cam atat pentru acum. Voi reveni cu noi solutii pentru probleme mai speciale.

Cosnita Radu!

vineri, 24 octombrie 2008

Accesare webservice din oracle

Aceasta problema mi se pare extrem de interesanta, dar foarte putin documentata pe internet. Am avut nevoie de asa ceva la unul din taskurile pe care trebuie sa le fac astazi pentru un proiect. Pentru rezolvarea problemei a trebuit intai sa creez un serviciu web de test. Intrucat este extrem de usor de creat si de publicat utilizand tehnologia J2EE am ales aceasta cale. Presupunand ca utilizati eclipse, creati un modul ejb. Dupa ce ati creat acest modul, adaugati urmatoarele clase/interfete in el:

ro.anaf.ws.HelloWorldWS

package ro.anaf.ws;

import javax.ejb.Remote;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
@SOAPBinding(style = Style.RPC)
@Remote
public interface HelloWorldWS
{
@WebMethod
public String helloWorld();

@WebMethod
public String receiveBinary(String s);
}


ro.anaf.ws.HelloWorldWSBean

package ro.anaf.ws;

import javax.ejb.Stateless;
import javax.jws.WebService;

@Stateless
@WebService(endpointInterface = "ro.anaf.ws.HelloWorldWS")
public class HelloWorldWSBean implements HelloWorldWS
{
public String helloWorld()
{
return "Salutari de pe WS";
}

public String receiveBinary(String s)
{
return "Am primat binarul: \"" + s + "\"";
}
}

Se poate remarca usor ca interfata reprezinta de fapt endpointul pentru serviciul web de test in timp ce clasa reprezinta implementarea operatiilor din endpoint. In acest moment serviciul web este implementat. Pentru a face deploy nu ramane decat sa creati un nou proiect de tip Enterprise Application si sa alegeti un server pe care sa faceti deploy. Personal, am folosit JBoss 4.2.3(ultima versiune stabila la aceasta data), dar puteti folosi orice alt container doriti. Dupa ce ati facut deploy la proiect puteti folosi adresa: http://localhost:8080/jbossws pentru a vizualiza lista de servicii web existente pe acest server. Ar trebui sa localizati si serviciul web pe care l-ati creat. In acest moment partea de java este incheiata.



Discutam acum de partea de oracle. Pentru inceput va trebui sa downloadati dbws-callout-utility-10131.zip. Dupa ce ati downloadat aceasta arhiva dezarhivati-o. In functie de versiunea utilizata va trebui sa executati una din comenzile de mai jos:
# 10gR2
loadjava -u scott/tiger -r -v -f -genmissing dbwsclientws.jar dbwsclientdb102.jar
# 11g
loadjava -u scott/tiger -r -v -f -genmissing dbwsclientws.jar dbwsclientdb11.jar
Obs: daca serverul nu ruleaza pe aceeasi masina pe care faceti development este posibil sa nu aveti utilitarul loadjava. Acesta este inclus in pachetul de developer ce poate fi downloadat gratuit de pe site-ul http://www.oracle.com.

Dupa ce ati executat pasii de mai sus, nu mai trebuie decat sa rulati scriptul urmator care realizeaza un test pentru metoda helloWorld:

declare
webservice utl_dbws.service;
webserviceCall utl_dbws.call;

wsdlURL VARCHAR2(1000) := 'http://10.18.14.32:8080/TestareServiciiEAR-ServiciiWeb/HelloWorldWSBean?wsdl';
webserviceNS VARCHAR2(1000) := 'http://ws.anaf.ro/';
webserviceName utl_dbws.qname;
webservicePort utl_dbws.qname;
webserviceOperation utl_dbws.qname;
params utl_dbws.anydata_list;
results anydata;
begin
webserviceName := utl_dbws.to_qname(webserviceNS, 'HelloWorldWSBeanService');
webservicePort := utl_dbws.to_qname(webserviceNS, 'HelloWorldWSBeanPort');
webserviceOperation := utl_dbws.to_qname(webserviceNS, 'helloWorld');

webservice := utl_dbws.create_service(wsdl_document_location => HTTPURITYPE(wsdlURL),
service_name => webservicename);

webserviceCall := utl_dbws.create_call(service_handle => webservice,
port_name => webservicePort,
operation_name => webserviceOperation);

results := utl_dbws.invoke(call_handle => webserviceCall, input_params => params);

utl_dbws.release_service(webservice);
utl_dbws.release_call(webserviceCall);

DBMS_OUTPUT.PUT_LINE(ANYDATA.AccessVarchar2(results));
end;


Setarile variabilelor: wsdlURL si webserviceNS le gasiti in wsdl. La fel si numele serviciului si numele portului. La executia blocului pl/sql de mai sus ar trebui sa primiti mesajul: 'Salutari de pe WS'.
La cele mentionate anterior mai exista un caz posibil care ar putea aparea: transmiterea parametrilor catre operatia serviciului web. Pentru a putea transmite parametrii trebuie sa utilizati vectorul params. De exemplu, pentru a adauga un parametru:
params(0):=ANYDATA.ConvertVarchar2('Cosnita Radu Viorel');
Pentru mai multe informatii consultati documentatia pentru ANYDATA.
Cam asta ar fi pentru azi. Nu ezitati sa adaugati comentarii despre acest articol.

Radu!

joi, 23 octombrie 2008

Despre ce va fi postat

Acesta este primul meu blog precum si primul meu post. Consider ca este momentul sa scriu despre anumite probleme pe care le intampin in domeniul IT, sperand ca rezolvarile propuse sa ajute si alti oameni din aceeasi industrie(stresanta si enervanta). Acestea fiind spuse, este timpul sa ma intorc la treaba ....... In cel mai scurt timp voi incepe sa postez probleme si rezolvari gasite.