wtorek, 21 grudnia 2010

Contexts and Dependency Injection – część II

Drugi z serii wpisów na temat CDI poświęcony będzie zdarzeniom. Pozwalają one luźno powiązać ze sobą bean'y na zasadzie Akcja → Reakcja.
Załóżmy, że nasza aplikacja chce odnotować w dowolnym miejscu moment operacji na obiekcie użytkownika. Należy w tym celu wstrzyknąć obiekt javax.enterprise.event.Event i wywołać na nim metodę fire(). Jej argumentem, będzie obiekt, który ma podlegać obserwacji.
@Named
@RequestScoped
public class UserBean{

    private List users;

    @Inject
    Event createUserEvent;


    @PostConstruct
    private void init(){
        
        users = new ArrayList();
        users.add(new User("Nowak"));
        users.add(new User("Lis"));
    }

    public void addUser(){

        User user = new User("Kowalski");
        users.add(user);
        createUserEvent.fire(user);
    }
}

Teraz wystarczy już w dowolnym miejscu aplikacji zaimplementować metodę nasłuchującą, będącą odbiorcą powyższego zdarzenia
@Named
@RequestScoped
public class UserObserver {

    public void onCreateUser(@Observes User user){

        System.out.println(">> User "+ user.getName() + " has been created");
    }
}

A co w przypadku gdybyśmy chcieli obserwować kilka różnych zdarzeń na obiekcie User ? Tutaj znowu z pomocą przychodzą kwalifikatory
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface CreateUser {
}
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface DeleteUser {
}

Zmodyfikowana klasa obserwująca
import javaspotlight.qualifiers.CreateUser;
import javaspotlight.qualifiers.DeleteUser;
import javax.enterprise.event.Observes;
import javax.faces.bean.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class UserObserver {

    public void onCreateUser(@Observes @CreateUser User user){

        System.out.println(">> User "+ user.getName() + " has been created");
    }

    public void onDeleteUser(@Observes @DeleteUser User user){

        System.out.println(">> User "+ user.getName() + " has been deleted");
    }
}

Sposób inicjacji zdarzeń – oczywiście nie muszą znajdować się one w tej samej klasie
import java.util.ArrayList;
import java.util.List;
import javaspotlight.qualifiers.CreateUser;
import javaspotlight.qualifiers.DeleteUser;
import javax.annotation.PostConstruct;
import javax.enterprise.event.Event;
import javax.faces.bean.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@RequestScoped
public class UserBean{

    private List users;

    @Inject
    @CreateUser
    Event createUserEvent;

    @Inject 
    @DeleteUser
    Event deleteUserEvent;

    @PostConstruct
    private void init(){
        
        users = new ArrayList();
        users.add(new User("Nowak"));
        users.add(new User("Lis"));
    }

    public void addUser(){

        User user = new User("Kowalski");
        users.add(user);
        createUserEvent.fire(user);
    }

    public void removeUser(){

        for(User user : users){
     
            if("Nowak".equals(user.getName())){

                deleteUserEvent.fire(user);
                users.remove(user);
            }
        }
    }

sobota, 4 grudnia 2010

Contexts and Dependency Injection – część I

Jako wielki entuzjasta Jboss Seam'a i korzystania z adnotacji, nie mogłem odpuścić sobie nauki specyfikacji CDI, (JSR-229), Jest to zbiór usług stanowiący integralną część standardu Java EE 6 i ułatwiający  powiązanie ze sobą poszczególnych komponentów (warstw) składających się  na powyższą platformę.

Contexts – przywiązanie komponentu Java EE do ściśle zdefiniowanego (ale rozszerzalnego) kontekstu (cyklu życia).
Dependency Injection – możliwość interakcji pomiędzy komponentami  aplikacji takimi jak serwlet, sesyjny EJB czy  JSF'owy managed bean poprzez wstrzyknięcie (Injection) czyli umieszczenie ich w dowolnym momencie w cyklu życia aplikacji.

Najlepiej naukę zacząć od praktycznego przykłady. Ja wykorzystam w tym celu środowisko NetBeans 6.9.1 z dostarczonym przez nie serwerem GlassFish 3.0.1. Zakładamy nowy nowy projekt: File › New Project › Web Application. Przy wyborze serwera należy pamiętać aby zaznaczyć opcję „Contexts and Dependency Injection”.
Powyższy krok, spowoduje nasza aplikacja będzie korzystać z implementacji CDI – WELD. W katalogu WEB-INF zostanie utworzony plik beans.xml, Jego istnienie sygnalizuje kontenerowi CDI, że aplikacja zawiera wstrzykiwane bean'y i musi być skanowana w poszukiwaniu klas zawierających adnotacje.

W kolejnym kroku z dostępnych technologii wybieram JSF

Celem aplikacji jest wstrzyknięcie do managed bean'a interfejsu odpowiedzialnego za logowanie.
public interface MyLogger {

    public void log(Class c, String message);
}

Implementacja wygląda następująco:
import javaspotlight.beans.MyLogger;
import javaspotlight.qualifiers.InfoLog;

public class InfoLogger implements MyLogger{

    @Override
    public void log(Class c, String message) {

      Logger.getLogger(c.getName()).info(message);
    }

Do wstrzyknięcia komponentu używamy adnotacji: @javax.inject.Inject
import java.util.logging.*;
import javaspotlight.beans.MyLogger;
import javaspotlight.qualifiers.InfoLog;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@RequestScoped
public class ExampleManagedBean {

    private String outputMessage;

    @Inject
    MyLogger myLogger;

    public String exampleAction(){
        
        outputMessage = "Hello";

        myLogger.log(getClass(), outputMessage);
        return null;
    }
    


    public String getOutputMessage() {
        return outputMessage;
    }

    public void setOutputMessage(String outputMessage) {
        this.outputMessage = outputMessage;
    }

W sumie trywialne. Co jednak się stanie jeśli nasz interfejs będzie posiadał więcej implementacji ? Kontener nie będzie wiedział, której z nich należy użyć. Problem ten rozwiązują kwalifikatory (Qualifiers). Pozwalają nam one na przypisanie dodatkowych informacji do naszego bean'a.

Ja utworzyłem dwie implementacjie loggera:
@InfoLog
public class InfoLogger implements MyLogger{

    @Override
    public void log(Class c, String message) {

      Logger.getLogger(c.getName()).info(message);
    }
}

@WarnLog
public class WarnLogger implements MyLogger{

    @Override
    public void log(Class c, String message) {

      Logger.getLogger(c.getName()).warning(message);
    }
}

Teraz aby móc wskazywać przy pomocy adnotacji na konkretny typ, należy utworzyć kwalifikatory
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface InfoLog {
}

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface WarnLog {
}

dzięki nim, wstrzykując nasz interfejs łatwo możemy wskazać jakiego typu bean zostać użyty.
@Inject
@WarnLog
MyLogger myLogger;

albo
@Inject
@InfoLog
MyLogger myLogger;

Struktura mojego projektu wygląda następująco:

piątek, 26 listopada 2010

News dla programistów Oracle ADF

Stało się. Oracle oficjalnie uruchomiło ścieżkę certyfikacji dla programistów ADF. Wiadomość opublikował wczoraj na swoim blogu Shay Shmeltzer. Egzamin oficjalnie nosi nazwę Oracle Application Development Framework Essentials (1Z1-554) i jak na razie jest w wersji beta – czyli ma służyć głównie wysądowaniu zainteresowania ścieżką certyfikacji z zakresu technologii Oracle Fusion oraz ustaleniu ostatecznego zakresu tematycznego. Dla bywalców forum dyskusyjnego OTN nie jest to zaskoczenie. Temat certyfikatów z ADF jest cyklicznie poruszany od co najmniej trzech lat. Na temat problematyki egzaminu można poczytać tutaj. Moje osobiste wrażenia są takie – sporo tego – nawet dla doświadczonego developera. Wrzucono do jednego worka w zasadzie całość technologii, lekko nawet poza nią wykraczając (ADF + SOA, WebCenter). Zresztą sam Shay napisał:
„...this is not a trivial exam - you should only apply if you actually have practical experience developing with ADF”.
Nie wiem też, jaka będzie forma tego egzaminu. Część pytań jest czysto teoretyczna, niektóre zagadnienia natomiast w zasadzie nie do opisania bez użycia JDeveloper'a. Cóż, wysłałem maila do autora postu i być może wkrótce będę wiedział coś więcej. Postaram się też skonfrontować zakres tematyczny egzaminu z tym, co napisane jest w dostępnej literaturze przedmiotu.

czwartek, 25 listopada 2010

Kolejny OTN Developer Day za mną

To był bez wątpienia jeden z bardziej produktywnie spędzonych ostatnio dni:). Naprawdę było warto. Tym razem cykliczna impreza organizowana przez Oracle poświęcona była m.in. cechom serwera GlassFish i planom jego rozwoju w przyszłości. Większa część zajęć, skupiona była jednak na nowościach w Javie EE 6, czyli EJB 3.1, JAX-RS, JSF 2.0, CDI, Servlet 3.0 i Java Persistence API 2.0. Wszystko oczywiście w wykorzystaniem powyższego serwera i NetBeans'a. Jedyny minus to ograniczony czas na zajęcia praktyczne, co zaskutkowało tym, że chyba nikt nie przerobił wszystkich ćwiczeń. Na szczęście każdy uczestnik dostał komplet zadań na płytce. Obiecałem sobie przerobić je wszystkie i oczywiście w miarę możliwości podzielić się tutaj wrażeniami.

P.S. Dzieląc się miedzy sobą opiniami na temat szóstki zgodnie stwierdziliśmy, że nie da się pisać zapytań przy pomocy Criteria API :).

sobota, 23 października 2010

Java Authentication and Authorization Service (JAAS) od podstaw, czyli jak stworzyć własny moduł logowania

Dzisiejszy wpis poświęcony będzie podstawom implementacji szkieletu JAAS odpowiedzialnego w Javie za autentykację do programu i autoryzację dostępu. Ideą JAAS jest swobodne łączenie modułów odpowiedzialnych za bezpieczeństwo - PAM (Pluggable Authentication Modules). Poniższy przykład zaczniemy od utworzenia klasy CustomPrincipal przechowującej nazwę naszego podmiotu (Subject) zgłaszającego prośbę o uwierzytelnienie - dla uproszczenia nazywajmy go dalej użytkownikiem. Musi ona implementować interfejsy: java.security.Principal i java.io.Serializable oraz zawierać metody toString() i equals(). Subject może przechowywać wiele obiektów Principal takich jak np: grupy, jednak na potrzeby niniejszego przykładu wykorzystamy tylko jeden.

package kuba.demo.jaas.module;

import java.io.Serializable;
import java.security.Principal;
import java.util.logging.Logger;

public class CustomPrincipal implements Principal, Serializable {
 
 private static Logger log =Logger.getLogger(CustomPrincipal.class.getName());
 private static final long serialVersionUID = 1L;
 
 private String name;

 
    public CustomPrincipal(String name) {
     
     if (name == null){
      log.severe("Brak principal name");
         throw new NullPointerException("Principal name is null");
     }
     this.name = name;
   }


    @Override
    public String getName() {
 return name;
     }

 
    public String toString() {
     return("CustomPrincipal-" + name);
    }


    public boolean equals(Object o) {
     if (o == null){
         return false;
     }
        if (this == o){
               return true;
        }
        
        if (!(o instanceof CustomPrincipal)){
                return false;
        }
        
        CustomPrincipal that = (CustomPrincipal)o;

     if (this.getName().equals(that.getName())){
         return true;
     }
    
     return false;
    }
     
    
    public int hashCode() {
     return name.hashCode();
    }
}


Kolejną wymaganą klasą jest CustomCallbackHandler - implementuje interfejs javax.security.auth.callback.CallbackHandler. Można powiedzieć, że jest ona pośrednikiem pomiędzy naszą aplikacją, a usługą logowania, przekazując jej nasze dane uwierzytelniające (credentials).

package kuba.demo.jaas.module;

import java.io.IOException;
import java.util.logging.Logger;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

public class CustomCallbackHandler implements CallbackHandler {
 
 private static Logger log =Logger.getLogger(CustomCallbackHandler.class.getName());
 
 private String user;
 private String password;

 public CustomCallbackHandler(String user, String password) {
  this.user = user;
  this.password = password;
 }

 @Override
 public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException {
  
  NameCallback nameCallback  = (NameCallback)callbacks[0];
  PasswordCallback passwordCallback = (PasswordCallback)callbacks[1];
  nameCallback.setName(user);
  passwordCallback.setPassword(password.toCharArray());
  log.info("Inicjalizacja callback handler'a dla uzytkownika: "+user);
 }
}

Pozostała jeszcze klasa odpowiadająca za uwierzytelnienie użytkownika czyli właściwy moduł logowania. Moduł taki - CutomLoginModule.java - musi implementować interfejs javax.security.auth.spi.LoginModule. Cała logika odpowiedzialna za uwierzytelnienie odbywa się w metodzie login(). Możemy w niej sięgnąć do bazy danych lub serwera usług katalogowych. Jeśli autentykacja nie powiedzie się, zwrócony zostanie wyjątek javax.security.auth.login.LoginException. Jeśli logowanie będzie poprawne wywoływana jest metoda commit(). Przesłaniając ją możemy przypisujemy naszemu uwierzytelnionemu obiektowi (Subject) odpowiednie właściwości. W przypadku gdy któraś z powyższych metod zwróci błąd, wywoływana jest metoda abort().

package kuba.demo.jaas.module;

import java.security.Principal;
import java.util.Map;
import java.util.logging.Logger;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

public class CustomLoginModule implements LoginModule{
 
 private static Logger log =Logger.getLogger(CustomLoginModule.class.getName());
 
 private Subject subject;
 private CallbackHandler callbackHandler;
 private Map sharedState;
 private Map options;
 private String userName;
 private String userPassword;
 
 
 @Override
 public void initialize(Subject subject, CallbackHandler callbackHandler,
         Map sharedState, Map options) {
  
  this.subject = subject;
  this.callbackHandler = callbackHandler;
  this.sharedState = sharedState;
  this.options = options;
 }

 
 @Override
 public boolean login() throws LoginException {
  
  NameCallback nameCallback = new NameCallback("Username");
  PasswordCallback passwordCallback = new PasswordCallback("Password",false);
  
  Callback[] callbacks = new Callback[]{ nameCallback, passwordCallback };
  
  try {
    callbackHandler.handle(callbacks);
    
  } catch (Exception e) { 
   e.printStackTrace();
  }
  
  userName = nameCallback.getName();
  userPassword = String.valueOf(passwordCallback.getPassword());
  
  if("jakub.pawlowski".equals(userName) && "my_password".equals(userPassword)){
   
   log.info("Poprawnie zalogowano uzytkownika [ "+userName+" ]");
   return true;
   
  }else{
   
   log.info("Logowanie nie powiodlo sie");
   return false;
  }
 }

 
 @Override
 public boolean commit() throws LoginException {
  
  Principal principal = new CustomPrincipal(userName);
  subject.getPrincipals().add(principal);
  userPassword = null;
  log.info("Dodano obiekt CustomPrincipal");
  
  return true;
 }

 
 @Override
 public boolean abort() throws LoginException {
  
  log.info("Wywolanie abort()");
  userName = null;
  userPassword = null;
  return true;
 }

 
 @Override
 public boolean logout() throws LoginException {
  
  log.info("Uzytkownik [ "+userName+" ] zostal wylogowany");
  userName = null;
  userPassword = null;
  return true;
 }

}

Ostatnim krokiem jest utworzenie pliku konfiguracyjnego wskazującego na mój moduł logowania. Ja nazwałem go custom_jaas.config

MyLoginModule
{
 kuba.demo.jaas.module.CustomLoginModule REQUIRED;
};

Czas na sprawdzenie jak to wszystko działa. Poniżej prosty klient, należy go uruchomić z opcją wskazującą na plik konfiguracyjny: -Djava.security.auth.login.config=="e:/custom_jaas.config"

package kuba.demo.jaas.client;

import java.security.Principal;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Logger;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import kuba.demo.jaas.module.CustomCallbackHandler;
import kuba.demo.jaas.module.CustomPrincipal;

public class CustomLoginModuleClient {

 private static Logger log =Logger.getLogger(CustomLoginModuleClient.class.getName());
 
 public static void main(String[] args) {
  
  CustomCallbackHandler callbackHandler = new CustomCallbackHandler("jakub.pawlowski", "my_password");
  
  try {
   LoginContext loginContext = new LoginContext("MyLoginModule",callbackHandler);
   loginContext.login();
   
   Subject s = loginContext.getSubject();
   Set ps = s.getPrincipals();
   Iterator iter = ps.iterator();
    
    while(iter.hasNext()){
     
     Principal principal = (Principal)iter.next();
     
     if(principal instanceof CustomPrincipal){
      
      log.info("Principal [ "+principal.getName()+" ]");
     }
    }
    
    loginContext.logout();
   
  } catch (LoginException e) {
   e.printStackTrace();
  }

 }
}


Jak widać poniżej autoryzacja przebiegła poprawnie

INFO: Poprawnie zalogowano uzytkownika [ jakub.pawlowski ]
2010-10-23 19:28:09 kuba.demo.jaas.module.CustomLoginModule commit
INFO: Dodano obiekt CustomPrincipal
2010-10-23 19:28:09 kuba.demo.jaas.client.CustomLoginModuleClient main
INFO: Principal [ jakub.pawlowski ]
2010-10-23 19:28:09 kuba.demo.jaas.module.CustomLoginModule logout
INFO: Uzytkownik [ jakub.pawlowski ] zostal wylogowany

niedziela, 17 października 2010

JSF 2.0 - Pierwsze starcie

Wczoraj po kilku godzinach prób, z przerwami na jedzenie, wymianę opon na zimowe, i z wieloma rzucanymi pod nosem niecenzuralnymi wyrazami, udało mi się doprowadzić do działania na serwerze Tomcat 7 aplikację JSF w wersji 2.0. Problemem było nie samo JSF ale obsługa Expression Language. Po przeczytaniu połowy internetu - tej większej:) - okazało się że do poprawnej obsługi EL API w ww. środowisku wymagana jest biblioteka el-ri-1.0.jar ( http://download.java.net/maven/2/com/sun/el/el-ri/1.0/ ). Pozostałe 2, czyli jsf-impl. jar i jsf-api.jar w wersji 2.0.3 pobrałem oczywiście stąd: https://javaserverfaces.dev.java.net/.

Cała pozostała konfiguracja to plik web.xml


Nie wiem czy to normalne ale JBoss Tools w wersji 3.2.0 M2 nie podpowiada na stronie nazw i składowych managed bean'ów, które nie są zarejestrowane w pliku faces-config.xml - co w JSF 2.0 nie jest wymagane ze względu na możliwość użycia adnotacji. Czyli coś takiego:

@ManagedBean(name = "myFirstBean")
@SessionScoped
public class TestBean implements Serializable{ 

 private static final long serialVersionUID = 1L;
 
 public TestBean() {
 }
}

nie jest widoczne na stronie .xhtml

niedziela, 10 października 2010

ADF Business Components i zatwierdzanie transakcji - problem z obsługą wyjątków

W tym tygodniu spędziłem parę godzin na znalezieniu problemu, czemu nagle po wgraniu patch'a na serwer aplikacyjny Oracle'a przestała działać jedna z funkcji. Wyglądała mniej wiecej tak:
public void addDepartment(Number deptno, String dname, String loc){
        
        ViewObject depts = getDeptView();
        Row newDept = depts.createRow();
        newDept.setAttribute("Deptno", deptno);
        newDept.setAttribute("Dname" , dname);
        newDept.setAttribute("Loc"   , loc);
        
        getDBTransaction().commit();
    }
czyli nie robiła nic nadzwyczajnego poza zapisem wiersza do tabeli w bazie. Problem oczywiście odnalazł się w logach serwera. Po zrobieniu upgrade'u z wersji 10.1.3.3 do wersji 10.1.3.5 okazało się, że identyfikator sesji nie ma już 44 znaków tylko 64, a moja funkcja zapisywała ten identyfikator do bazy. Niestety framework nie propagował tego wyjątku do warstwy widoku-controlera i nie dodawał go do kolejki FacesMeassages. Oczywiście prawidłowo napisana funkcja w module aplikacji powinna wyglądać następująco:
public void addDepartment(Number deptno, String dname, String loc){
        
        ViewObject depts = getDeptView();
        Row newDept = depts.createRow();
        newDept.setAttribute("Deptno", deptno);
        newDept.setAttribute("Dname" , dname);
        newDept.setAttribute("Loc"   , loc);
        
        try{
            
            getDBTransaction().commit();
            
        }catch(JboException ex){
            
              getDBTransaction().rollback();
              throw new JboException(ex);
        }
    }
Jak widać, wbrew marketingowi, Oracle ADF nie robi za nas wszystkiego :). Warto o tym pamiętać, szczególnie wywołując procedury PL/SQL.

wtorek, 28 września 2010

Integracja aplikacji JSF z Eclipse Birt z wykorzystaniem Scripting API

Niniejszy wpis dotyczyć będzie integracji aplikacji JSF (w moim przypadku to RichFaces) z technologią raportowania Eclipse Birt. Moja aplikacja ma podpiętą przeglądarkę Birt Viewer, która służy mi do pobrania raportów w formacie PDF czy Excel. To co mnie interesowało to przekazanie do raportu listy pracowników oraz nagłówka.

Poniżej kod mojego managed beana:

Kluczowy dla komunikacji pomiędzy moją aplikacją a silnikiem Birt'a jest poniższy fragment kodu:





Czas na handlery - czyli klasy odpowiadające za podstawienie przesłanych danych do szablonu raportu. Pierwszy odpowiada z przechwycenie parametrów:

















Drugi odpowiada za przechwycenie listy wierszy tabeli:



































W obu przypadkach widać, że do danych przesłanych do raportu uzyskujemy dostęp dzięki klasie IReportContext.
Teraz wystarczy już tylko utworzyć w raporcie źródło danych - w moim przypadku będzie to Scripted Data Source oraz korzystający z tego źródła Data Set.

Należy też dodać parametr - będący nagłówkiem raportu





















Na koniec trzeba jeszcze podłączyć do raportu utworzone wcześniej handlery:








środa, 22 września 2010

Web Services – usługa i klient z wykorzystaniem Apache Axis 2 i Eclipse Galileo

Mój zbliżający się wielkimi krokami projekt oparty będzie o usługi sieciowe w związku z czym, postanowiłem rozpoznać temat i zobaczyć jakie jest wsparcie w Eclipse i co sprawdzi się lepiej – Apache Axis 2 czy JAX-WS (Java API for XML Web Services). Zacznijmy od Axisa – nie wymaga in kontenera EJB w związku z czym użyłem Tomcata 6.
W Eclipse wybieramy File → New → Dynamic Web Project
Do utworzenia mojego Web Service'u wybrałem podejście bottom-up czyli utworzenie usługi z klasy Java. Alternatywnie można wybrać podejście top-down czyli utworzenie usługi na bazie istniejącego dokumentu WSDL.
Poniżej moja klasa z jedną metodą
public class AxisDemoService {
 
 public String sayHello(String name){
  return "Hello "+name;
 } 
}

Następnie wybieramy File –> New → Web Service
W następnym kroku należy wybrać funkcje dostępne dla naszej usługi
 
i wystartować server
To czy usługa została poprawnie uruchomiona można sprawdzić pod adresem: http://localhost:8080/AxisWS/services
Czas na utworzenie aplikacji klienckiej. Ja zrobię to w osobnym projekcie o nazwie AxisWSClient.
Ponownie wybieramy File → New → Dynamic Web Project , a następnie File –> New → Web Service Client. Wskazujemy na lokalizację naszego dokumentu WSDL:
Poniżej wygenerowany z automatu kod mojej aplikacji klienckiej.
 
Czas na mały test. Utworzyłem w tym celu dodatkową klasę
import java.rmi.RemoteException;

public class AxisDemoServiceTest {

    public static void main(String[] args) throws RemoteException {
        
        AxisDemoServiceProxy proxy = new AxisDemoServiceProxy();
        proxy.setEndpoint("http://localhost:8080/AxisWS/services/AxisDemoService?wsdl");
        
        String message = proxy.sayHello("Kuba");
        System.out.println(message);

    }
} 

Warto jeszcze zabezpieczyć usługę. W tym celu wykorzystałem najprostszą z możliwych autentykację typu BASIC. W pliku konfiguracyjnym tomcat-users.xml dodałem użytkownika

  
  


i stworzyłem odpowiednie wpisy w pliku web.xml

   
    Axis Services
    /services/*
   
   
    ws_role
   
  
  
   BASIC
   Axis WS Realm
  

Moja klasa testowa w aplikacji klienckiej także wymagała małej modyfikacji
public class AxisDemoServiceTest {

 public static void main(String[] args) throws RemoteException {
  
  AxisDemoServiceProxy proxy = new AxisDemoServiceProxy();
  proxy.setEndpoint("http://localhost:8080/AxisWS/services/AxisDemoService?wsdl");
  
  AxisDemoServiceSoapBindingStub stub = (AxisDemoServiceSoapBindingStub)proxy.getAxisDemoService();
        stub.setUsername("ws_user");
        stub.setPassword("ws_password");
  
  String message = proxy.sayHello("Kuba");
  System.out.println(message);

 }
}

Działa.

niedziela, 19 września 2010

Hello World II - czy nowa odsłona mojego bloga

Witam !
Zbierałem się do tego długo, aż wreszcie w ten weekend spiąłem się zrobiłem nowego bloga. Skłoniło mnie do tego parę rzeczy, a między innymi fakt, że już od dłuższego czasu nie zajmuję się programowaniem w ADF. Doszły do tego pewne problemy administracyjne oraz dość kłopotliwe i zajmujące dla mnie tworzenie wpisów dwóch językach. Jeśli komuś przydały się zawarte w nich rozwiązania, cieszę się bardzo i mam nadzieję że i tutaj zajrzy od czasu do czasu – tym bardziej, że tematyka dalej kręcić będzie się wokół Javy.

Moje ambitne plany :)
Od teraz chcę skupić się bardziej konkretnie na Java EE. Mam nadzieję, że wkrótce pojawi się coś na temat WebServices. Oczywiście będzie sporo o moim ulubionym zagadnieniu czyli JSF i różnych implementacjach tego standardu – tym bardziej, że w ostatniej wersji 2.0 pojawiło się sporo nowych rzeczy. Na pewno będzie coś z tematyki ORM czyli EJB i Hibernate, a może i MyBatis - who knows ?. Z pewnością znajdą się też wpisy dotyczące JBoss Seam, którym jestem już od dłuższego czasu zafascynowany. Mam zamiar co nieco wspomnieć o bardziej zaawansowanych możliwościach Eclipse Birt i jego integracji z aplikacjami webowymi. No i oczywiście bezpieczeństwo aplikacji – tutaj także chciałbym się troszeczkę wykazać.
Jeśli chodzi o bazy danych to pozostaje przy jedynie słusznej czyli Oracle, aczkolwiek żeby nikt nie był zszokowany jeśli zobaczy coś o Informix'ie – tak, istnieje jeszcze coś takiego :).
Z tematów całkowicie dla mnie nowych planuję powalczyć z portalem Liferay i samymi portletami. Postaram się zamieszczać cykliczne relacje z tej walki, licząc na cenne uwagi i komentarze ze strony czytelników.
Czy będzie jeszcze o Oracle ADF ? - być może. Od czasu do czasu jeszcze coś w nim dłubię.

P.S.
Właśnie przeczytałem to co napisałem wyżej. Ciekawe kiedy znajdę na to wszystko czas :).