niedziela, 23 maja 2010

Integracja JSF i Spring

Przygotowanie środowiska
Tradycyjną drogą integracji JSF i Springa było odwoływanie się z managed beans do spring beans z użyciem tradycyjnych metod wstrzykiwania kontekstu aplikacji. W poniższym artykule pokażę jak można zastąpić managed beans których używamy w technologi JSF, spring beanami które tworzy i udostępnia Spring.
Zacznijmy więc od ściągnięcia odpowiednich bibliotek do Springa które możemy umieścić w katalogu WebContent/WEB-INF/lib naszej aplikacji (zakładam że dysponujesz już jakąś aplikacją z wykorzystaniem JSF, jeśli nie to polecam zajrzeć najpierw do artykułu JSF + Tomcat 6 + Eclipse - Hello World). Linki do potrzebnych bibliotek zamieszam poniżej:

Spring - stworzenie beana
Zacznijmy od stworzenia prostego beana springowego. Do tego celu potrzebować będziemy prostej klasy javowej z jedną metodą (getHello()) która wypisz na ekran napisz powitalny (pamiętajmy o tym że bean ten będzie wykorzystany w JSF dlatego metody wypisujące napis na ekran mają prefiks get).
package com.blogspot.mkorwel.spring;

import org.springframework.stereotype.Component;

/**
 * 
 * @author Mateusz Korwel
 *
 */
@Component
public class HelloBean {

 public String getHello() {
  return "Hello Spring";
 }

}

Klasa HelloBean jest opatrzona adnotacją @Component co sugeruje że będzie to bean springowy. Następnym krokiem konfiguracji springa będzie stworzenie pliku konfiguracyjnego. W moim przykładzie plik ten znajduję się w katalogu WebContent/WEB-INF i nazywa się applicationContext.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 <context:component-scan base-package="com.blogspot.mkorwel.spring" />

</beans>

Jest to tradycyjny plik konfiguracyjne do springa, z wpisem który wskazuje springowi w jakich pakietach znajdują się spring beany (czyli wszystkie klasy które opatrzone są adnotacją @Component o której już wspomniałem).

Integracja
Do integracji Springa i JSF będziemy musieli zmodyfikować plik WebContent/WEB-INF/web.xml i WebContent/WEB-INF/faces-config.xml. Do pliku web.xml dodajemy informacje o springowym kontekście aplikacji:
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <listener>
  <listener-class>
   org.springframework.web.context.request.RequestContextListener</listener-class>
 </listener>

 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/applicationContext.xml</param-value>
 </context-param>
a do pliku faces-config.xml informację o wykorzystanie spring beanów jako managed beanów
<application>
  <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
 </application>

Użycie nowej funkcjonalności
Kiedy wszystko jest już skonfigurowane możemy wykorzystywać nasze spring beany jako managed beany, do tego celu stworzyłem prostą stronkę hello.jsp (w katalogu WebContent):
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f"  uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h"  uri="http://java.sun.com/jsf/html"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>JSF Spring</title>
</head>
<body>
<f:view>
<h:outputText value="#{helloBean.hello}"></h:outputText>
</f:view>
</body>
</html>

Po umieszczeniu naszej aplikacji (nazwa mojej aplikacji to JSFSpring) na serwerze i uruchomieniu (http://localhost:8080/JSFSpring/hello.jsf) na stronie powinien pojawić się napisz Hello Spring.

środa, 19 maja 2010

Adnotacje

Adnotacje (ang. annotations) są dostępne w javie od wersji 1.5 i służą do opisywania klas. W poniższym artykule opowiem jak szybko stworzyć i wykorzystać własne adnotacje.

Tworzenie adnotacji
Definicja pustej adnotacji nie różni się specjalnie od definicji pustego interfejsu. Pomijając znaczek @ w słówku @interface w zasadzie nie ma żadnej różnicy.
package com.blogspot.mkorwel.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationMethod {

}
Stworzenie adnotacji wymaga jeszcze użycia metaadnotacji. Poniżej umieszczam krótki opis:
  • @Target określa miejsce stosowania adnotacji
  • @Retention określa trwałość adnotacji
  • @Documented ujęcie danej adnotacji w dokumencie Javadoc
  • @Inherited zezwolenie na dziedziczenie adnotacji w podklasach

Możliwe argumenty dla metaadnotacji @Target to:
  • ElementType.TYPE - w deklaracji klasy, interfejsu, adnotacji, enum
  • ElementType.CONSTRUCTOR - w deklaracji konstruktora
  • ElementType.FIELD - w deklaracji pola
  • ElementType.LOCAL_VARIABLE - w deklaracji zmiennej lokalnej
  • ElementType.METHOD - w deklaracji metody
  • ElementType.PACKAGE - w deklaracji pakietu
  • ElementType.PARAMETER - w deklaracji parametru

Możliwe argumenty dla metaadnotacji @Retention to:
  • RetentionPolicy.SOURCE - adnotacje unieważnione przez kompilator
  • RetentionPolicy.CLASS - adnotacje widoczne w plikach klas, unieważnione przez maszynę wirtualną
  • RetentionPolicy.RUNTIME - adnotacje podtrzymywane w maszynie wirtualnej w czasie wykonania, a więc nadające się do odczytu za pośrednictwem refleksji

Poniżej stworzymy jeszcze jedną adnotację która będzie używana przy deklaracji klas (@Target(ElementType.TYPE)). Adnotacja będzie zawierała dwa elementy. Poniżej kod:
package com.blogspot.mkorwel.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotationClass {

 public int id() default -1;
 
 public String name();
 
}
Elementy w adnotacjach deklarujemy podobnie do metod w intefejsie. Każdy element może mieć wartość domyślną, aby ustalić taką wartość używamy słówka default.

Wykorzystanie adnotacji
Po stworzeniu dwóch naszych adnotacji, możemy użyć ich w naszej klasie. Kod klasy nie jest zbyt skomplikowany, więc nie będę go opisywał.
package com.blogspot.mkorwel;

import com.blogspot.mkorwel.annotation.MyAnnotationClass;
import com.blogspot.mkorwel.annotation.MyAnnotationMethod;

@MyAnnotationClass(id = 1, name = "Mateusz")
public class MyClass {
 
 @MyAnnotationMethod
 public void test(){
  
 }

}

Kiedy mamy już adnotacje i klasę która jest nimi opisana, możemy stworzyć klasę która wykorzysta nasze adnotacje i wypisze komunikat na ekranie. Kod klasy która to robi znajduje się poniżej:
package com.blogspot.mkorwel;

import com.blogspot.mkorwel.annotation.MyAnnotationClass;

/**
 * 
 * @author Mateusz Korwel
 * 
 */
public class Test {

 public static void main(String[] args) {

  // tworzenie klasy
  MyClass myClass = new MyClass();
  Class clazz = myClass.getClass();

  // pobranie adnotacji w klasie
  for (int i = 0; i < clazz.getAnnotations().length; i++) {
   // wypisanie adnotacji
   System.out.println("Nazwa adnotacji: " + clazz.getAnnotations()[i]);
   System.out.println("Wartość atrybutu id: "
     + ((MyAnnotationClass) clazz.getAnnotations()[i]).id());
   System.out.println("Wartość atrybutu name: "
     + ((MyAnnotationClass) clazz.getAnnotations()[i]).name());
  }

  // pobranie wszystkich publicznych/pakietowych metod z klasy MyClass
  for (int i = 0; i < clazz.getMethods().length; i++) {
   // pobranie wszystkich adnotacji przypisanych do metody
   for (int j = 0; j < clazz.getMethods()[i].getAnnotations().length; j++) {
    // nazwa metody z adnotacją
    System.out.println("\nNazwa metody z adnotacją: "
      + clazz.getMethods()[i].getName());
    // wypisanie adnotacji
    System.out.println("Nazwa adnotacji: "
      + clazz.getMethods()[i].getAnnotations()[j]);
   }
  }
 }
}
Krótko opiszę co powyższa klasa robi. W pierwszej pętli for pobieramy wszystkie adnotacje które są przypisane do klasy. W naszym przypadku jest to tylko jedna adnotacja @MyAnnotationClass z dwoma elementami które zostaną również wypisane. Druga pętla for pobiera metody jakie znajdują się w klasie, a następnie pobiera adnotacje przypisane do kolejnych metod. W naszym przypadku będzie wypisana jedna metoda test() z jedną adnotacją @MyAnnotationMethod.

niedziela, 9 maja 2010

EJB + Servlet: Prosta aplikacja enterprise

Przygotowanie środowiska pracy.
Do stworzenia aplikacji użyłem:
- Eclipse jako IDE
- JBoss jako serwer aplikacji na którym będzie uruchamiana cała aplikacji

Projekt EAR
Zaczniemy od stworzenia Enterprise Application Project (EAR). Aplikacja będzie zawierała dwa moduły:
  • EJB - projekt zawierajacy ziarna EJB
  • WAR - projekt zaweirający cały widok aplikacji
To właśnie tą aplikację będziemy deployować na serwerze JBoss.

W Eclipse Enterprise Application Project tworzymy w następujący sposób. Klikamy w File -> New -> Other ... i wybieramy Enterprise Application Project.

Następnie wybieramy Target runtime, wpisujemy nazwę aplikacji i klikamy Finish.

Projekt jest na razie pusty i zostanie uzupełniony w dalszej części artykułu.

Projekt EJB
EJB czyli Enterprise JavaBeans, aby móc korzystać z tej technologi musimy stworzyć projekt EJB. W Eclipse tworzymy go w następujący sposób: Klikamy w File -> New -> Other ... i wybieramy EJB Project.

Następnie wpisujemy nazwę projektu, wybieramy wersję EJB (my będziemy korzystać z wersji 3.0) oraz zaznaczamy że nasz projekt będzie częścią dużej aplikacji enterprise.

Po kliknięciu Next > odznaczamy chęć stworzenia EJB Client JAR.

W momencie gdy mamy już przygotowany projekt możemy utworzyć pierwsze ziarno EJB. Tak więc tworzymy zwykły interfejs w dowolnym pakiecie. U mnie wygląda to tak:
package com.blogspot.mkorwel.ejb;

import javax.ejb.Local;

/**
 * 
 * @author Mateusz Korwel
 * 
 */
@Local
public interface HelloLocal {

 public String getHelloWorld();
}

Interfejs kończy się sufiksem Local, co sugeruje że interfejs jest lokalny(w EJB występuje również interfejs Zdalny (Remote)). Rodzaj interfejsu deklarujemy używając adnotacji @Local lub @Remote.

Teraz tworzymy klasę która będzie implementację interfejsu HelloLocal.
package com.blogspot.mkorwel.ejb;

import javax.ejb.Stateless;

@Stateless
public class HelloBean implements HelloLocal {

 @Override
 public String getHelloWorld() {

  return "Hello World";
 }

}

Aby klasa stała się ziarnem EJB wystarczy użyć adnotacji @Stateless lub @Stateful. Adnotację określają czy ziarno EJB jest bezstanowe (Stateless) czy stanowe (Stateful). My tworzymy komponent bezstanowy dlatego użyliśmy adnotacji @Stateless. Nasza metoda getHelloWorld() zwraca prosty napis Hello World który wypiszemy w serwlecie.

Projekt WAR
Następnym krokiem do stworzenia naszej przykładowej aplikacji jest utworzenie projektu dla warstwy widoku, czyli zwykłego projektu WEB'owego w którym będą znajdowały się strony HTML, JSP, Servlety itd. W Eclipse klikamy w File -> New -> Other ... i wybieramy Dynamic Web Project.

Wpisujemy nazwę i nie zapominamy o dodaniu projektu do naszej aplikacji enterprise.

Następnie klikamy Finish. Teraz musimy jeszcze ustawić zależność w projekcie, tak aby mieć dostęp do klas z projektu EJB. Robimy to poprzez kliknięcie lewym przyciskiem myszy na projekcie helloWAR, wybranie Java Build Path a następnie Projects.

Klikamy w Add aby dodać zależność do projektu. Wybieramy nasz project EJB czyli helloEJB. Klikamy OK.


Teraz tworzymy zwykły servlet (w mnie nazywa się Hello), który wypisze prosty napis Hello World który zdefiniowaliśmy w projekcie EJB. Kod servletu wygląda następująco.
package com.blogspot.mkorwel.ejb;

import java.io.IOException;
import java.io.PrintWriter;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 
 * @author Mateusz Korwel
 * 
 */
public class Hello extends HttpServlet {
 private static final long serialVersionUID = 1L;

 @EJB
 private HelloLocal hello;

 public Hello() {
  super();
 }

 protected void doGet(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  PrintWriter out = response.getWriter();

  out.println(hello.getHelloWorld());
 }

}

Jak widzimy jest to zwykły servlet który wywoła nam metodę getHelloWorld() z interfejsu HelloLocal. Za pomocą adnotacji @EJB wstrzykniemy implementację naszego interfejsu (klasę HelloBean.java która znajduje się w projekcie helloEJB).

Tak przygotowaną aplikację może zdeployować na serwerze JBoss. Aplikacja będzie znajduje się po linkiem 127.0.0.1:8080/helloWAR/Hello. Zauważmy że deployujemy aplikację EAR, a w przeglądarce odwołujemy się projektu WAR. Dzieje się tak dlatego że projekt helloWAR jest modułem aplikacji helloEAR.

sobota, 1 maja 2010

JavaMail: Wysłanie emaila

W poniższym artykule zademonstruje jak łatwo można napisać kod, który wysyła wiadomości email. Wysyłanie wiadomości odbędzie się przy pomocy naszego konta na gmail'u, więc nie będzie potrzebne instalowanie serwerów smtp, itd.

Przygotowanie projektu
Pierwszym krokiem jakim musisz wykonać jest założenie sobie konta na googlach. Następnie potrzebujemy jeszcze biblioteki Java Mail dzięki której będziemy mogli wysyłać maile. Plik jar możemy ściągnąć z oficjalnej strony Java Mail Api. Po ściągnięciu i rozpakowaniu, plik mail.jar przenosimy do katalogu bibliotek w naszym projekcie.

Proste wysłanie maila
Oto prosty kod dzięki któremu wyślemy bardzo prostą wiadomość:
package com.blogspot.mkorwel.mail;

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

/**
 * 
 * @author Mateusz Korwel
 * 
 */
public class SendMail {

 private static final String HOST = "smtp.gmail.com";
 private static final int PORT = 465;
 // Adres email osby która wysyła maila
 private static final String FROM = "mateusz.korwel@gmail.com";
 // Hasło do konta osoby która wysyła maila
 private static final String PASSWORD = "xxx";
 // Adres email osoby do której wysyłany jest mail
 private static final String TO = "mateusz.korwel@gmail.com";
 // Temat wiadomości
 private static final String SUBJECT = "Hello World";
 // Treść wiadomości
 private static final String CONTENT = "To mój pierwszy mail wysłany za pomocą JavaMailAPI.";

 public static void main(String[] args) {
  try {
   new SendMail().send();
  } catch (MessagingException e) {
   e.printStackTrace();
  }
 }

 public void send() throws MessagingException {

  Properties props = new Properties();
  props.put("mail.transport.protocol", "smtps");
  props.put("mail.smtps.auth", "true");

  // Inicjalizacja sesji
  Session mailSession = Session.getDefaultInstance(props);

  // ustawienie debagowania, jeśli nie chcesz oglądać logów to usuń
  // linijkę poniżej lub zmień wartość na false
  mailSession.setDebug(true);

  // Tworzenie wiadomości email
  MimeMessage message = new MimeMessage(mailSession);
  message.setSubject(SUBJECT);
  message.setContent(CONTENT, "text/plain; charset=ISO-8859-2");
  message.addRecipient(Message.RecipientType.TO, new InternetAddress(TO));

  Transport transport = mailSession.getTransport();
  transport.connect(HOST, PORT, FROM, PASSWORD);

  // wysłanie wiadomości
  transport.sendMessage(message, message
    .getRecipients(Message.RecipientType.TO));
  transport.close();
 }
}

Powyższy kod wysyła bardzo prostą wiadomość email. A co jeśli chcieli byśmy sformatować naszą wiadomość przy pomocy znaczków html? Powyższy kod wysłał by całą zawartość maila bez żadnej transformacji, ale wystarczy że zmienimy typ mime naszej wiadomości i treść zostanie sformatowana. Obecnie mamy text/plain a potrzebujemy text/html. Po takiej zmianie możemy wysyłać maila z kodem html.

Wysłanie maila z załącznikiem
Oto prosty kod dzięki któremu wyślemy wiadomość z załącznikiem:
package com.blogspot.mkorwel.mail;

import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.FileDataSource;
import javax.activation.DataHandler;

import java.util.Properties;

/**
 * 
 * @author Mateusz Korwel
 * 
 */
class SendMailAttachment {

 private static final String HOST = "smtp.gmail.com";
 private static final int PORT = 465;
 // Adres email osby która wysyła maila
 private static final String FROM = "mateusz.korwel@gmail.com";
 // Hasło do konta osoby która wysyła maila
 private static final String PASSWORD = "xxx";
 // Adres email osoby do której wysyłany jest mail
 private static final String TO = "mateusz.korwel@gmail.com";
 // Temat wiadomości
 private static final String SUBJECT = "Hello World";
 // Treść wiadomości
 private static final String CONTENT = "To mój pierwszy mail z załącznikami wysłaby za pomocą JavaMailAPI.";
 // Ścieżka do pliku
 private static final String PATH_FILE = "zalacznik.txt";

 public static void main(String[] args) {
  try {
   new SendMailAttachment().send();
   System.out.println("Wiadomość wysłana");
  } catch (MessagingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 void send() throws MessagingException {

  Properties props = new Properties();
  props.put("mail.transport.protocol", "smtps");
  props.put("mail.smtps.auth", "true");

  // Pobranie sesji
  Session mailSession = Session.getDefaultInstance(props, null);

  // Tworzenie wiadomości
  MimeMessage message = new MimeMessage(mailSession);
  message.setSubject(SUBJECT);

  // Stworzenie części wiadomosci z treścią
  MimeBodyPart textPart = new MimeBodyPart();
  textPart.setContent(CONTENT, "text/html; charset=ISO-8859-2");

  // Stworzenie części z załacznikami
  MimeBodyPart attachFilePart = new MimeBodyPart();
  FileDataSource fds = new FileDataSource(PATH_FILE);
  attachFilePart.setDataHandler(new DataHandler(fds));
  attachFilePart.setFileName(fds.getName());

  // Zestawienie obydwu części maila w jedną wieloczęściową
  Multipart mp = new MimeMultipart();
  mp.addBodyPart(textPart);
  mp.addBodyPart(attachFilePart);

  // Dodanie treści maila
  message.setContent(mp);
  message.addRecipient(Message.RecipientType.TO, new InternetAddress(TO));

  Transport transport = mailSession.getTransport();
  transport.connect(HOST, PORT, FROM, PASSWORD);

  // Wysąłnei maila
  transport.sendMessage(message, message
    .getRecipients(Message.RecipientType.TO));
  transport.close();
 }
}

Powyższego kodu chyba nie trzeba komentować. Jedną różnicą maila z załącznikami od maila bez załączników jest to, że wiadomość składa się z kilku części.