Akt I
W przykładzie zostanie użyta prosta, lekko okrojona encja z mojego projektu zaliczeniowego.package org.gen2.biblioteka.encje;
@Entity
public class Autor implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String imie;
private String nazwisko;
public Autor() {
super();
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getImie() {
return imie;
}
public void setImie(String imie) {
this.imie = imie;
}
public String getNazwisko() {
return nazwisko;
}
public void setNazwisko(String nazwisko) {
this.nazwisko = nazwisko;
}
public String toString() {
return imie + " " + nazwisko;
}
}
Akt II
Definiujemy prosty interfejs (z jedna metoda) dla bean'a który będzie naszym WebSerwisem (tudzież Usługą Sieciową)package org.gen2.biblioteka.beany;
@WebService
@SOAPBinding(style = Style.DOCUMENT)
public interface AutorWSInterface {
ArrayListpobierzAutora(int idAutora);
}
@WebService - oznacza, ze intefejs będzie służył do implementacji WebSerwisu
@SOAPBinding - definiuje sposób komunikacji pomiędzy 'klientem' a 'serwerem', nie zaleca sie mieszanie style = RPC z use = LITERAL (JBoss w wersji 5.0 nie obsługuje tego jeszcze poprawnie)
Akt III
A teraz sama implemetacja bean'a.package org.gen2.biblioteka.beany;
@Stateless
@WebService(endpointInterface = "org.gen2.biblioteka.beany.AutorWSInterface")
@SOAPBinding(style = Style.DOCUMENT)
public class AutorWSBean {
private @PersistenceContext(unitName="BibliotekaEJB")
EntityManager em;
@WebMethod
public ArrayListpobierzAutora(int idAutora) {
Query query = em.createQuery("select a from Autor as a where a.id = " + idAutora);
ArrayListautor = new ArrayList ();
Autor temp = (Autor) query.getSingleResult();
autor.add(temp.getImie());
autor.add(temp.getNazwisko());
return autor;
}
}
@WebService(endpointInterface = "org.gen2.biblioteka.beany.AutorWSInterface") - musi wskazywać na intefejs który klasa ma implementować.
@WebMethod - jest użyteczne gdy mamy wiecej metod w beanie i chcemy żeby tylko niektóre były "eksportowane" do WSDL'a.
Akt IV
A teraz jak tego użyc?Najpierw musimy wrzucić nasz projekt na serwer, następnie tworzymy w eclipsie nowy projekt, naciskamy Ctrl + N, z menu wybieramy 'Web Services', 'Web Service Client', w 'Service Definition' wklejamy link do naszego WSDL (link możemy znalezć na stronie, nie musimy sie przekopywać przez tony logów tak jak np. w Geronimo), klikamy 'Finish' i mamy utworzony interfejs do połączenia z naszym WebSerwisem, teraz napiszemy klase w ktorej przetesujemy jego dzialanie.
public class Test {
public static void main(String[] args) {
AutorWSBeanService abs = new AutorWSBeanServiceLocator();
try {
AutorWSInterface asi = abs.getAutorWSBeanPort();
String[] autor = asi.pobierzAutora(1);
System.out.println(autor[0] + " " + autor[1]);
} catch (ServiceException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Happyend
Uruchamiamy powyższą aplikacje lokalnie. A poniżej mamy wynik działania programu.Adam Mickiewicz
Tu byl wielki babol rzeczowy;)
Fajnie czyta się taki krótki przepis na usługę sieciową. Na prawdę relaksujące ;-)
OdpowiedzUsuńKilka uwag:
public Autor() {
super();
}
niepotrzebne, bo będzie dostarczony domyślnie przez kompilator.
Po co @SOAPBinding(style = Style.DOCUMENT) przy interfejsie i implementacji?
Nie prościej implementować (realizować) interfejs w ten sposób pozbywając się atrybutu enpointInterface?
Jeśli persistence.xml zawiera pojedynczą definicję PU to można opuścić atrybut unitName w @PersistenceContext.
Publiczne metody bezstanowego ziarna EJB są automatycznie metodami usługi sieciowej - niepotrzebna @WebMethod przy pobierzAutora.
Od dzisiaj "select a from Autor as a where a.id = " + idAutora jest zabronione! tego nie wolno Ci robić! Uważaj na atak typu "SQL injection". Warto skorzystać z nazwanych zapytań (named queries) i parametrów zapytania. Rozpracowanie tematu zostawiam jako pracę domową ;-)
Dlaczego metoda pobierzAutora zwraca ArrayList, w którym będą dwa ciągi? Nie prościej po prostu zwrócić Autora?!
"musi wskazywać na intefejs" - nic nie musi - patrz komentarz wyżej.
"a nie każdy musi mieć implemetacje list" - a skąd to ograniczenie? Jaki język nie posiada listy i potrafi korzystać z usług sieciowych? Tutaj czuję ograniczenie na wyrost. Oczekuję wyjaśnień ;-)
Jacek
Wszystkie materiały które znalazłem na ten temat były pobieżne, ew dość zawile opisane.
OdpowiedzUsuń1.
public Autor() {
super();
}
Narzut z eclipsa
2.
@SOAPBinding(style = Style.DOCUMENT)
Racja, niepotrzebne, pozostałość po moich eksperymentach z różnymi style i use
3.
"Jeśli persistence.xml zawiera pojedynczą definicję PU to można opuścić atrybut unitName w @PersistenceContext."
To jest spowodowane moja niewiedzą. "Tak nas uczono". Dzięki za uwagę.
4.
"select a from Autor as a where a.id = " + idAutora.
Zaczerpniete z zajec, u hmm.. imienia nie bede chyba wymienial;) Bylem swiecie przekonany ze JEE, tak jak znienawidzony pehap potrafi escape'owac zapytania i ustrzec sie przed SQL inj...
5.
"a nie każdy musi mieć implemetację list"
Przykład: pomimo tego ze python ma niezgorszą implementacje list, nie potrafi poprawnie wyciągnąć odpowiedzi od javowego webserwisu, a o (o zgrozo!) obiekcie Autor juz nie mówiąc (niezależnie od przestrzeni nazw, bo to chyba od tego po części zależy), zwaliłem to ograniczenia WSDL.
Po chwili zastanowienia dochodze do wniosku, iż NamedQuery w moim wypadku sa zbedne, poniewaz uzytkownik w żaden sposob nie moze jawnie nic przekazać do zapytania, te wszystkie id albo sa wybierane z predefiniowanych list, albo sa wyklikane na podstawie istniejacego wpisu pobranego z richfacesowego data table. Aczkolwiek obiecuje ze bede omijal czyste zapytania sqlowe szerokim lukiem;)
OdpowiedzUsuńZapominasz jednak, że haker nie będzie korzystał z interfejsu webowego, a użyje własnego klienta, który korzysta z danych spreparowanych - zero wybierania z listy z RichFaces, czy innego cuda. Zapomnij o tym. NamedQuery są konieczne, obowiązkowe i co tam jeszcze sobie można wymyśleć. Poza tym, zapytania mogłyby trafić do adnotacji @NamedQuery, albo orm.xml, a nie być zanurzone gdzieś w kodzie.
OdpowiedzUsuńW końcu doszukałem się dlaczego nasze zapytania są złe. Z tego co wyczytałem chodzi nie tyle o named query co o bindowanie.
OdpowiedzUsuńQuery query = em.createQuery( "select p from Person p where p.name = :name");
query.setParameter( "name", name );
Przez interfejs webowy masz na myśli same strony czy wliczamy w to MBeany ?
Interfejs webowy, ale nie z przeglądarki, tylko z jakiegokolwiek innego narzędzia obsługującego http, w której nie uruchamiasz kontrolnego kodu javascript.
OdpowiedzUsuńZgadzam się że namedQuery to jedynie słuszne rozwiązanie przy wykorzystaniu bezpośredniego interfejsu aplikacji. Jest tylko jeden problem z webServicami pod JBossem - potrzebują JBoss'a ;). Proponuje inne rozwiązanie oparte na springframework i dowolnym kontenerze serwletów oraz cxf jako implementacji standardu
OdpowiedzUsuńfajny blog pokazujący konkretne problemy i ich rozwiązania - szkoda że tak szybko przestałeś pisać - może jest szansa żeby tu wrócić przecież blogów o pisaniu programów jest w polskim internecie jak na lekarstwo
OdpowiedzUsuń