wtorek, 27 kwietnia 2010

Wysłanie SMS w Javie

Zaczniemy od ściągnięcia dwóch niezbędnych bibliotek:
Po ściągnięciu i rozpakowaniu pliki .jar kopiujemy do projektu i ustawiamy zależności.

Założenie bezpłatnego konta na ipipi.com
Do wysyłania sms będziemy potrzebowali serwera smtp. Do tego celu możemy założyć konta na ipipi.com. Założenie konta jest darmowe. Do konta dostaniemy możliwość wysłania 3 testowych wiadomości, za kolejne trzeba już niestety zapłacić (kupno pakietu).
Wchodzimy na stronę rejestracji i wypełniamy wszystkie potrzebne pola (login, hasło, email, telefon, imię, nazwisko i kraj). Na podany numer telefony zostanie nam wysłana wiadomość z kodem dostępu który będziemy musieli podać w następnym etapie rejestracji.
Strona rejestracji

Kod klasy
Po założeniu konta możemy napisać naszą pierwsza klasę do wysyłania sms'ów za pośrednictwem ipipi.com.

package com.blogspot.mkorwel.sms;

import java.util.Properties;
import java.util.Date;
import javax.mail.*;
import javax.mail.internet.*;

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

 // login z portlau ipipi.com
 private static String USERNAME = "xxx";
 // hasło z portalu ipipi.com
 private static String PASSWORD = "xxxyyyzzz123";
 private static String SMTP_HOST = "ipipi.com";
 // podajemy nazwe swojego konta w serwicie ipipi.com
 private static String FROM = "xxx@ipipi.com";
 // podajemy numer na który wysyłamy sms'a
 private static String TO = "+48123456789@sms.ipipi.com";
 // Wpisujemy temat naszej wiadomości
 private static String SUBJECT = "Moja pierwsza wiadomość";
 // Wpisujemy temat naszej wiadomości
 private static String BODY = "Hello World!!!";

 public static void main(String[] args) {

  SMSSend smsSend = new SMSSend();
  smsSend.sendSMS();
  System.out.println("WIADOMOSZ WYSLANA");

 }

 public void sendSMS() {

  Transport tr = null;

  try {
   // ustawiamy parametry
   Properties props = System.getProperties();
   props.put("mail.smtp.auth", "true");

   // pobieramy sesję obiektu
   Session mailSession = Session.getDefaultInstance(props, null);

   // Tworzymy wiadomość
   Message msg = new MimeMessage(mailSession);
   msg.setFrom(new InternetAddress(FROM));
   msg.setSubject(SUBJECT);
   msg.setText(BODY);
   msg.setSentDate(new Date());

   // ustawiamy odbiorcę wiadomości
   InternetAddress[] address = { new InternetAddress(TO) };
   msg.setRecipients(Message.RecipientType.TO, address);

   tr = mailSession.getTransport("smtp");
   tr.connect(SMTP_HOST, USERNAME, PASSWORD);
   msg.saveChanges();

   // wysyłamy sms'a
   tr.sendMessage(msg, msg.getAllRecipients());
   tr.close();
  } catch (Exception e) {
   System.out.println("Error");
   e.printStackTrace();
  }
 }
}

Jeśli wszystko poszło dobrze możemy cieszyć się wysyłaniem sms'ów.

czwartek, 15 kwietnia 2010

Spring Hello World

W poniższym artykule pokaże jak w bardzo prosty sposób użyć Springa. Do tworzenia kodu użyłem edytora Eclipse z zainstalowaną wtyczką SpringIDE (podaję linka za pomocą którego możemy zainstalować plugin w eclipse http://dist.springframework.org/release/IDE).


Przygotowanie środowiska
Na początek musimy ściągnąć z internetu następujące jary:


Całą paczkę jarów ze springiem można sciągnąć z oficjalnej strony springa.
Kiedy już będziemy mieli je na dysku, proponuję stworzyć w projekcie oddzielny katalog na biblioteki np. lib i umieścić tam ściągnięte jary. Następnie dodajemy biblioteki do projektu.

Tworzenie beana
Zaczniemy od stworzenia prostej klasy która będzie springowym beanem. Tak więc tworzymy pakiet com.blogspot.mkorwel a w nim klase Osoba.java. W klasie Osoba tworzymy pole imie typu String wraz z metodami get i set. Całość powinna wyglądać tak:

package com.blogspot.mkorwel;

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

 private String imie;

 public void setImie(String imie) {
  this.imie = imie;
 }

 public String getImie() {
  return imie;
 }

}

Prosta klasa jest gotowa, aby stała się springowym beanem musimy umieścić odpowiedni wpis w odpowiednim pliku konfiguracyjnym. Tak wiec za pomocą wtyczki SpringIDE tworzymy Spring Bean Configuration File. Klikamy na projekcie prawym przyciskiem, wybieramy New -> Other... -> Spring -> Spring Bean Configuration File




klikamy Next > 




po wpisaniu nazwy (u mnie beans.xml) i kliknięciu Next >




musimy jeszcze wybrać schemę, beans -> spring-beans-3.0.xsd. Po tym wszystkim powinien stworzyć się prosty plik xml w którym będziemy umieszczać wszystkie beany. Cały plik po dodaniu wpisu z naszą klasą Osoba powinien wyglądać tak:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

 <bean id="osoba" class="com.blogspot.mkorwel.Osoba">
  <property name="imie" value="Mateusz"></property>
 </bean>

</beans>

W elemencie bean tworzymy instancje danej klasy według wzorca Singleton (czyli obiekt jednej instancji wykorzystywany w całej aplikacji). W atrybucie id deklarujemy identyfikator beana, a w class podajemy ścieżkę do pliku. Po takiej deklaracji mamy już zainicjalizowaną naszą klasę i możemy jej używać wszędzie w aplikacji. Element property jest opcjonalny i mówi o wartościach z jakimi tworzony jest bean (wywołanie metody setImie (atrybut name) z wartością Mateusz (atrybut value)).

Stworzenie prostego JUnite
Aby przetestować czy bean tworzy się poprawnie, tworzymy prosty JUnite test w którym będziemy wstrzykiwać naszą instancję klasy i sprawdzać czy wartość w polu imię jest Mateusz.
Cały kod testu ma wygląda tak:

package com.blogspot.mkorwel;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 
 * @author Mateusz Korwel
 * 
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:beans.xml" })
public class OsobaTest {

 @Autowired
 @Qualifier("osoba")
 private Osoba osoba;

 @Test
 public void testImie() {

  Assert.assertEquals("Mateusz", osoba.getImie());

 }

}


Jak widzimy jest to zwykły test JUnit z kilkoma springowymi adnotacjami. Najważniejsza adnotacja to oczywiście @ContextConfiguration która wstrzykuje nam kontekst naszej aplikacji. Możemy sami podać miejsce gdzie jest zlokalizowany nasz plik konfoguracyjny beans.xml poprzez ustawienie pola locations, jesli tego nie zrobimy to aplikacja odwoła się do pliku który znajduje się w tym samym pakiecie co klasa o nazwie naszej klasy z końcówką -context.xml (w naszym wypadku był by to plik com/blogspot/mkorwel/OsobaTest-context.xml). Kolejne dwie ważne adnotacje to @Autowired@Qualifier. Adnotacja @Autowired wstrzykuje nam instancję klasy Osoba do pola osoba, a @Qualifier wskazuje na konkretne id instancji (w wypadku gdy plik beans.xml zawierał by kilka instancji klasy Osoba) która ma zostać wstrzyknięta. Adnotacja @RunWith wskazuje nam klasę z jaką ma się uruchomić test.

poniedziałek, 12 kwietnia 2010

Proste parsowanie XML w Javie

Co to jest XML?
XML (Extensible Markup Language) czyli niezależny od platformy sposób przechowywania, przekazywania pewnej porcji informacji.
Poniżej bardzo prosty przykład pliku XML zawierającego informacje o osobach (imię, nazwisko, płeć):
<?xml version="1.0"?>
<osoby>
  <osoba plec="mężczyzna">
   <imie>Jan</imie>
   <nazwisko>Kowalski</nazwisko>
  </osoba>
  <osoba plec="kobieta">
   <imie>Anna</imie>
   <nazwisko>Nowak</nazwisko>
  </osoba>
</osoby>


Jak skutecznie przetworzyć XML wykorzystując język Java?
Jest wiele API których można użyć do parsowania XML'i, ja pokaże dwa z nich:


  • DOM
  • SAX
DOM
Pierwszy sposób przetwarzania plików XML to Document Object Model, w skrócie DOM. Prosty API dzięki któremu możemy w łatwy sposób przeszukać plik XML i wyciągnąć z niego interesujące nas informacje. Plik XML jest wczytywane w całości do pamięci i odpowiednimi metodami możemy wyciągać z niego informacje nas interesujące. Metoda ta jest oczywiście niezalecana w przypadku dużych plików XML. Zaletą tego rozwiązania jest to, że w każdej chwili mamy dostęp do całego drzewa dokumentu, czyli do wszystkich elementów, atrybutów, wartości, itd.
Poniżej prosty kawałek kodu który czyta plik XML podany we wstępie artykułu.
package com.blogspot.xml.dom;

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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

  public static void main(String argv[]) {

   try {
   File file = new File("myFile.xml");
   DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
   DocumentBuilder db = dbf.newDocumentBuilder();
   Document doc = db.parse(file);
   doc.getDocumentElement().normalize();

    System.out.println("Element główny: "
     + doc.getDocumentElement().getNodeName());
   
   NodeList nodeLst = doc.getElementsByTagName("osoba");

    for (int s = 0; s < nodeLst.getLength(); s++) {

     Node fstNode = nodeLst.item(s);

     if (fstNode.getNodeType() == Node.ELEMENT_NODE) {

      Element fstElmnt = (Element) fstNode;

      NodeList fstNmElmntLst = fstElmnt
       .getElementsByTagName("imie");
     Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
     NodeList fstNm = fstNmElmnt.getChildNodes();

      NodeList lstNmElmntLst = fstElmnt
       .getElementsByTagName("nazwisko");
     Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
     NodeList lstNm = lstNmElmnt.getChildNodes();

      System.out.println("\n***");
     System.out.println("Atrybut plec: "
       + fstElmnt.getAttribute("plec"));
     System.out.println("Element Imię : "
       + ((Node) fstNm.item(0)).getNodeValue());
     System.out.println("Element Nazwisko : "
       + ((Node) lstNm.item(0)).getNodeValue());
    }

    }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}
SAX
Poznaliśmy już bardzo prostą metodę parsowania XML'i łającą pliki do pamięci operacyjnej komputera. Ale co jeśli mamy bardzo duży plik XML którego nie chcieli byśmy w całości wczytywać? W takim wypadku możemy użyć z SAX'a, czyli Simple API for XML. Określamy szereg metod, które wywołują się podczas parsowania pliku XML. Przetwarzanie metodą SAX jest jednokierunkowe.
Poniżej proste dwie klasy korzystające z SAX:
package com.blogspot.xml.sax;

import org.xml.sax.*;
import org.xml.sax.helpers.*;

/**
 * 
 * @author Mateusz Korwel
 * 
 */
public class Handler extends DefaultHandler {

  @Override
 public void startElement(String namespaceURI, String localName,
   String qualifiedName, Attributes att) throws SAXException {
  System.out.println("Znalezione element: " + qualifiedName);
  if (qualifiedName.equals("employee")) {
   System.out.println("Znalezione atrybut: " + att.getValue("name"));
  }
 }

  @Override
 public void characters(char ch[], int start, int length)
   throws SAXException {

   System.out.println("Treść elementu: " + start + " to "
    + (start + length - 1) + ": " + new String(ch, start, length));
 }

}
package com.blogspot.xml.sax;

import javax.xml.parsers.*;
import org.xml.sax.XMLReader;

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

  public static void main(String args[]) throws Exception {

   // Tworzenie parsera
  SAXParserFactory spf = SAXParserFactory.newInstance();
  spf.setNamespaceAware(true);
  SAXParser saxParser = spf.newSAXParser();
  XMLReader parser = saxParser.getXMLReader();

   // Tworzenie klasy Handler
  Handler handler = new Handler();
  parser.setContentHandler(handler);

   // Zaczęćie parsowania dokumentu
  parser.parse("myFile.xml");
 }
}

czwartek, 25 marca 2010

JAAS - prosty moduł

W jednym z poprzednim wpisie, pokazywałem jak wykorzystać JAAS'a w aplikacji webowej. Teraz chciałbym się skupić na tym, jak napisać własny moduł, dzięki któremu będziemy mogli wprowadzić własne zasady uwierzytelnianie i autoryzacji. Na końcu pokażę jak użyć tego modułu w aplikacji webowej, uruchomionej na serwerze aplikacji JBoss.


Kilka słów o samej strukturze
Prosty moduł będzie składał się z 3 klas:

  • MyPrincipal
  • MyGroup
  • MyLoginModule
Klasy należy umieścić gdzieś w naszej aplikacji, u mnie będzie to pakiet com.blogspot.mkorwel.jaas




MyPrincipal
MyPrincipal jest prostą klasą implementującą interfejs java.security.Principal. Klasa przechowuje identyfikator (login, publiczne id, email) który pozwala jednoznacznie określić użytkowania lub rolę.
package com.blogspot.mkorwel.jaas;


import java.io.Serializable;
import java.security.Principal;


/**
 * 
 * @author mkorwel
 *
 */
public class MyPrincipal implements Principal, Serializable {


 /**
  * 
  */
 private static final long serialVersionUID = 1933073722894960007L;
 private final String name;


 public MyPrincipal(String name) {
  this.name = name;
 }


 public String getName() {
  return name;
 }


 public boolean equals(Object o) {
  if (o == null)
   return false;


  if (this == o)
   return true;


  if (!(o instanceof MyPrincipal))
   return false;
  MyPrincipal that = (MyPrincipal) o;


  if (this.getName().equals(that.getName()))
   return true;
  return false;
 }


 public int hashCode() {
  return name.hashCode();
 }
}
MyGroup
MyGroup jest prostą klasą implementującą interfejsy java.security.acl.Groupjava.io.Serializable. Klasa przechowuje kolekcję ról.
package com.blogspot.mkorwel.jaas;


import java.io.Serializable;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;


/**
 * 
 * @author mkorwel
 *
 */
public class MyGroup implements Group, Serializable {


 /**
  * 
  */
 private static final long serialVersionUID = -3978735590793434379L;
 private final String name;
 private final Set users = new HashSet();


 public MyGroup(String name) {
  this.name = name;
 }


 public boolean addMember(Principal user) {
  return users.add(user);
 }


 public boolean removeMember(Principal user) {
  return users.remove(user);
 }


 public boolean isMember(Principal member) {
  return users.contains(member);
 }


 public Enumeration members() {
  return Collections.enumeration(users);
 }


 public String getName() {
  return name;
 }


 public boolean equals(Object o) {
  if (o == null)
   return false;


  if (this == o)
   return true;


  if (!(o instanceof MyGroup))
   return false;
  MyGroup that = (MyGroup) o;


  if (this.getName().equals(that.getName()))
   return true;
  return false;
 }


 public int hashCode() {
  return name.hashCode();
 }


}

MyLoginModule
Klasa MyLoginModule jest miejscem gdzie implementujemy całą logikę modułu. Klasa implementuje interfejs LoginModule, czyli 5 metod:
  • initialize
  • login
  • commit
  • abort
  • logout
Initialize 
Metoda initialize wywoływana jest przy każdej próbie logowania. Zajmuje się wczytaniem wszystkich niezbędnych parametrów (takich jak miejsce zapisu loginów, haseł, itd.)

Login
Metoda odpowiada za poprawną uwierzytelnianie. W tradycyjnym modelu uwierzytelniania metoda sprawdza login i hasło.

Commit
Metoda wykonuje się jeżeli uwierzytelnianie powiodło się. Zapisuje osobę i role do obiektu Subject.

Abort
Metoda wykonuje się jeżeli uwierzytelnianie się nie powiodło.

Logout
Metoda wywoływana podczas wylogowania użytkownika.

package com.blogspot.mkorwel.jaas;


import java.util.HashMap;
import java.util.Map;


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.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;


/**
 * 
 * @author mkorwel
 * 
 */
public class MyLoginModule implements LoginModule {


 private Map usernameAndPassword = new HashMap() {


  private static final long serialVersionUID = -2695491152140640122L;


  {
   put("user1", "user");
   put("admin", "admin");
  }
 };
 private Map usernameAndRoles = new HashMap() {


  private static final long serialVersionUID = 1136203053244663646L;


  {
   put("user1", "user");
   put("admin", "admin");
  }
 };


 // initial state
 private Subject subject;
 private CallbackHandler callbackHandler;
 @SuppressWarnings("unused")
 private Map sharedState;
 @SuppressWarnings("unused")
 private Map options;


 // username and password
 private String username;
 private String password;


 private MyPrincipal userPrincipal;


 @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 {


  Callback[] callbacks = new Callback[2];
  callbacks[0] = new NameCallback("user name: ");
  callbacks[1] = new PasswordCallback("password: ", false);


  try {
   // pobranie loginu
   callbackHandler.handle(callbacks);
   username = ((NameCallback) callbacks[0]).getName();


   // pobranie hasla
   char[] tmpPassword = ((PasswordCallback) callbacks[1])
     .getPassword();
   if (tmpPassword == null) {
    tmpPassword = new char[0];
   }
   password = new String(tmpPassword);


   ((PasswordCallback) callbacks[1]).clearPassword();


  } catch (java.io.IOException ioe) {
   throw new LoginException(ioe.toString());
  } catch (UnsupportedCallbackException uce) {
   throw new LoginException("Error: " + uce.getCallback().toString()
     + " not available to garner authentication information "
     + "from the user");
  }


  // weryfikacje loginu i hasla
  return (usernameAndPassword.containsKey(username) && password
    .equals(usernameAndPassword.get(username)));
 }


 @Override
 public boolean commit() throws LoginException {


  userPrincipal = new MyPrincipal(username);


  if (!subject.getPrincipals().contains(userPrincipal)) {
   subject.getPrincipals().add(userPrincipal);
   MyGroup role = new MyGroup("Roles");
   role.addMember(new MyPrincipal(usernameAndRoles.get(username)));
   subject.getPrincipals().add(role);
  }


  return true;
 }


 @Override
 public boolean abort() throws LoginException {


  username = null;
  password = null;
  userPrincipal = null;


  return true;
 }


 @Override
 public boolean logout() throws LoginException {


  subject.getPrincipals().remove(userPrincipal);


  username = null;
  password = null;
  userPrincipal = null;


  return true;
 }
}
Nasz prosty moduł nie posiada żadnych parametrów przekazywanych z zewnątrz. Korzysta z bardzo prostego mechanizmu uwierzytelniania login/hasło które są zapisane w dwóch mapach na początku klasy.
Ważna uwaga!!!
Jeśli używamy serwera aplikacyjnego JBoss, tak jak ja w przykładzie, musimy pamiętać o tym  że wszystkie role muszę być wewnątrz obiektu "Roles".

Własny moduł pod JBoss'em
Tak jak wspominałem na początku, pokaże jak szybko wykorzystać nasz moduł w aplikacji uruchomionej na serwerze aplikacji JBoss.
Jeśli korzystamy ze standardowego modułu uwierzytelniania i autoryzacji jaki przedstawiłem w poprzednim wpisie, to musimy podmienić tylko plik jboss-service.xml. Zmiany dotyczą tylko wskazania klasy modułu i usunięciu wszystkich dodatkowych parametrów takich jak: wskazanie zapytania SELECT dzięki któremu pobieramy login/hasło/role, itd.
Aktualny jboss-service.xml powinien wyglądać tak:
<?xml version="1.0" encoding="UTF-8"?>
<server>
 <mbean code="org.jboss.security.auth.login.DynamicLoginConfig"
  name="jboss.seminarium:name=seminarium.login,service=DynamicLoginConfig">


  <attribute name="PolicyConfig" serialDataType="jbxb">
   <jaas:policy
    xsi:schemaLocation="urn:jboss:security-config:4.1 resource:security-config_4_1.xsd"
    xmlns:jaas="urn:jboss:security-config:4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <jaas:application-policy name="my-auth-module">
     <jaas:authentication>
      <jaas:login-module code="com.blogspot.mkorwel.jaas.MyLoginModule"
       flag="required">
      </jaas:login-module>
     </jaas:authentication>
    </jaas:application-policy>
   </jaas:policy>
  </attribute>
  <depends optional-attribute-name="LoginConfigService">
   jboss.security:service=XMLLoginConfig
        </depends>
  <depends optional-attribute-name="SecurityManagerService">
   jboss.security:service=JaasSecurityManager
        </depends>
 </mbean>
</server>

niedziela, 7 marca 2010

Użycie recaptcha w javie

Recaptcha
Zaczynamy od stworzenia sobie konta na recaptcha i dodajemy domenę na której nasz kod będzie używany. Jeśli nie zamierzamy udostępniać naszej aplikacji w internecie, to możemy wymyślić sobie tymczasową domeną, np. global-key.pl. Musimy tylko pamiętać aby zaznaczyć checkbox (Enable this key on all domains (global key)) dzięki któremu będziemy mogli korzystać z naczego klucza także na serwerach produkcyjnych czy testowych (takich jak localhost czy 127.0.0.1). Następnie klikamy Create Key.
Wszystko powinno wyglądać mniej więcej tak:


Do implementacji recaptcha będziemy potrzebowali biblioteki recaptcha4j-0.0.7.jar. Plik jar należy umieścić w katalogi WebContent\WEB-INF\lib lub w innym katalogu gdzie przechowujemy biblioteki dla naszej aplikacji. 

Przygotowanie formularza
Kiedy mamy już wygenerowany prywatny i publiczny klucz, czas wstawić do formularza pola które będą przechowywać wartości recaptcha. Do tego celu użyjemy tego skryptu napisanego w JavaScript:
<script type="text/javascript"
   src="http://api.recaptcha.net/challenge?k=<your_public_key>">
</script>


<noscript>
   <iframe src="http://api.recaptcha.net/noscript?k=<your_public_key>"
       height="300" width="500" frameborder="0"></iframe><br>
   <textarea name="recaptcha_challenge_field" rows="3" cols="40">
   </textarea>
   <input type="hidden" name="recaptcha_response_field" 
       value="manual_challenge">
</noscript>

W powyższym skrypcie wstawiamy nasz Public Key w miejsce <your_public_key> i tak przygotowany fragment kodu wklejamy do naszego formularza. U mnie wygląda to tak:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Login</title>
</head>
<body>
 <form action="success" method="post">


  <div>
   Value1: <input type="text" name="value1"/>
  </div>
  
  <div>
   Value2: <input type="text" name="value2"/>
  </div>
  
  <div>
   <script type="text/javascript"
      src="http://api.recaptcha.net/challenge?k=<publicKey>">
   </script>
   <noscript>
       <iframe src="http://api.recaptcha.net/noscript?k=<publicKey>"
           height="300" width="500" frameborder="0"></iframe><br />
       <textarea name="recaptcha_challenge_field" rows="3" cols="40">
       </textarea>
       <input type="hidden" name="recaptcha_response_field" 
           value="manual_challenge" />
   </noscript>
  </div>
  
  <div>
   <input type="submit" value="Wyslij" />
  </div>
 
 </form>
</body>
</html>





Możemy teraz uruchomić naszą aplikację i sprawdzić czy recaptcha działa, na tym etapie powinna się tylko wyświetlić na stronie.
Nadszedł czas na stworzenie logiki biznesowej która odbierze formularz z recaptchą. Ja do tego celu użyję serwletu. 


package com.blogspot.mkorwel.servlet;


import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
/**
 * 
 * @author mkorwel
 *
 */
public class Success extends HttpServlet {
  private static final long serialVersionUID = 1L;


  protected void doGet(HttpServletRequest request,
    HttpServletResponse response) throws ServletException, IOException {


  }


  protected void doPost(HttpServletRequest request,
    HttpServletResponse response) throws ServletException, IOException {


   //Pobranie i wypisanie naszych danych z formularza
   System.out.println(request.getParameter("value1"));
   System.out.println(request.getParameter("value2"));
   
   //Pobranie danych dla recaptcha
   String challengeReCaptcha = request.getParameter("recaptcha_challenge_field");
   String responseReCaptcha = request.getParameter("recaptcha_response_field");
   String remoteAddr = request.getRemoteAddr(); //Address użytkonika
      
   //Wypisanie danych dla recaptcha
   System.out.println(challengeReCaptcha);
   System.out.println(responseReCaptcha);
   System.out.println(remoteAddr);
   
   ReCaptchaImpl reCaptcha = new ReCaptchaImpl();
   //ustawiamy nasz klucz prywatny
      reCaptcha.setPrivateKey("<privateKey>");
      
      //sprawdzamy czy użytkownik wpisał pobrany wyraz w recaptcha
      ReCaptchaResponse reCaptchaResponse =
          reCaptcha.checkAnswer(remoteAddr, challengeReCaptcha, responseReCaptcha);
      
      if (!reCaptchaResponse.isValid()) {
       response.sendRedirect("/index.jsp");
      } else {
       getServletConfig().getServletContext().getRequestDispatcher(
    "/result.jsp").forward(request, response);
      }
   
  }
}

Krótkie wyjaśnienie co dzieje się w servlecie.
W metodzie doPost na początku pobieramy nasze dane z formularza aby wypisać je na ekran. Następnie pobieramy dane dla recaptcha, tj. dwa pola które umieściliśmy na stronie: recaptcha_challenge_field i recaptcha_response_field oraz adres użytkownika który wpisywał recaptcha. Po wczytaniu potrzebnych danych tworzymy obiekt ReCaptchaImpl i ustawiamy nasz klucz prywatny. W tym momencie mamy już wszystkie dany które potrzebne nam są do zweryfikowania poprawnoście recaptcha, dlatego tworzymy obiekt ReCaptchaResponse na którym wywołujemy metode checkAnswer z klasy ReCaptchaImpl którą kilka linijek temu stworzyliśmy. Do sprawdzenia czy użytkownik wpisał pobrany wyraz dla recaptcha używamy metody isValid. W Serwlecie w razie błędnego wpisania kodu, użytkownik zostaje przekierowany do strony z formularzem, a jeśli kod jest poprawny, idzie do strony która wyświetla ekran powitalny.

wtorek, 23 lutego 2010

JSF + Tomcat 6 + Eclipse - Hello World

Konfiguracja Apache Tomcat 6
Zaczniemy od przygotowani środowiska pracy. Potrzebujemy oczywiście zainstalowanego Tomcat'a oraz pliku jstl-1.2.jar. Plik jar ściągamy i kopiujemy do katalogu tomcat_home\lib.

Pierwsza aplikacja Java Serwer Faces
Uruchamiamy eclipse i tworzymy nowy Dynamic Web Project.

Wpisujemy nazwę naszego produktu i ustawiamy:

  • Target runtime: na Apache Tomcat v6.0
  • Dynamic web model version: na 2.5
  • Configuration: na JavaSerwer Faces v1.2 Project

Wszystko powinno wyglądać mniej więcej tak:

Następnie klikamy Next > do momentu aż pojawi się okienko z ustawieniami JSF. Klikamy na Download library.
Po krótkiej chwili pojawi się okienko z dostępnymi bibliotekami do JSF. Wybieramy JSF 1.2 (Apache MyFaces) i klikamy Next >.
Akceptujemy licencję i klikamy Finish.

Dodajemy do URL Mapping Patterns: *.jsf.

Jeśli wszystkie kroki wykonaliśmy prawidłowo wszystko powinno wyglądać tak:


Właśnie stworzyliśmy strukturę projektu. Jak pewnie już zauważyliście jest to normalny Web Projekt z katalogiem Web Content w którym znajduje się katalog WEB-INF z plikiem web.xml. Jest jednak mała różnica, w katalogu WEB-INF znajduje się plik o nazwie faces-config.xml. Jest to plik gdzie znajduje się cała konfiguracja odnośnie JSF. Na razie plik ten jest pusty. 
Zaczniemy od stworzenia prostej klasy zarządzającej treścią strony, tzw. bean, warto zwrócić tutaj uwagę że klasa powinna być napisana zgodnie z prostymi zasadami nazewniczymi dla metod zwracających (getNazwaZmiennej) i ustawiających (setNazwaZmiennej) . W katalogu src tworzymy pakiet com.blogspot.mkorwel.jsf a w nim klasę MyBean. Klasa będzie posiadać funkcję która zwróci nam napis "Hello World !!!".

package com.blogspot.mkorwel.jsf;


/**
 * 
 * @author mkorwel
 * 
 */
public class MyBean {


  private String hello = "Hello World !!!";


  public String getHello() {


   return this.hello;
  }

}

Żeby klasę MyBean móc wykorzystywać w JSF musimy dodać ją w faces-config.xml. Otwiera plik XML na zakładce ManagedBean,


zaznaczamy w lewym okienku session i klikamy Add.


Klikamy przycisk Browse... i wybieramy klasę com.blogspot.mkorwel.jsf.MyBean.


W tym momencie możemy już kliknąć Finish. Zapisujemy wprowadzone zmiany i zabieramy się do stworzenia podstawowego widoku, który wyświetli nam napis "Hello World !!!" z klasy MyBean.
W katalogu WebContent tworzymy hello.jsp w którym wyświetlimy napis. Plik u mnie wygląda tak:
<%@ taglib prefix="f"  uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h"  uri="http://java.sun.com/jsf/html"%>
<html>
<head>
<title>JSF Tutorial</title>
</head>
<body>
<f:view>
<h:outputText value="#{myBean.hello}"></h:outputText>
</f:view>
</body>
</html>
W pierwszych dwóch linkach naszej strony definiujemy dwa zbiory znaczników.
Wszystkie znaczniki technologii JSF znajdują się pomiędzy znacznikiem <f:view>.
Znacznik <h:outputText value="#{myBean.hello}"></h:outputText> wyświetla tekst na ekranie, w atrybucie value precyzujemy skąd ma być wzięta treść. W naszym przypadku będzie to klasa która w pliku faces-config.xml nazywa się myBean (czyli klasa com.blogspot.mkorwel.jsf). Po kropce definiujemy metode typu get która ma się wykonać, w naszym przypadku będzie to metoda getHello(), która zwróci "Hello World !!!".

W tym momencie cała aplikacja jest gotowa, wiec możemy ją zdeployować na serwerze. Stronę możemy obejrzeć pod adresem http://localhost:8080/JSFHelloWorld/faces/hello.jsp , http://127.0.0.1:8080/JSFHelloWorld/faces/hello.jsphttp://localhost:8080/JSFHelloWorld/hello.jsf, lub http://127.0.0.1:8080/JSFHelloWorld/hello.jsf.

piątek, 19 lutego 2010

Eclipse + SVN

Jeśli zaczynamy jakiś projekt grupowy i zdecydowaliśmy się pisać go pod Eclipem, to warto zainteresować się również SVN (Subversion), który jest prostym i skutecznym systemem kontroli wersji. Dzięki temu bez problemu będziemy mogli wymieniać się plikami, każdy będzie miał dostęp do aktualnej i działającej wersji naszego projektu.

Instalujemy wtyczkę subclipse.

Otwieramy eclipsa i wchodzimy w Help -> Install New Software ...

Po kliknięciu w przycisk Add... wyskakuje małe okienko, w którym proszeni jesteśmy o podanie nazwy (Name:) oraz lokalizacji (Location:) wtyczki jaką chcemy zainstalować. My instalujemy wtyczkę do SVN, dlatego w pierwszy polu wpisujemy svn, w drugim podajemy lokalizację, czyli http://subclipse.tigris.org/update_1.6.x.


Klikamy w OK, a w nowo otwartym oknie zaznaczamy wszystkie checkboxy i wciskamy Next >.


Teraz instalator podsumowuje listę tego co chcemy zainstalować. Klikamy jeszcze raz Next >, po czym akceptujemy licencję i kliknąć przycisk Finish. Teraz musimy chwilę poczekać, jeśli podczas instalacji pojawia się jakieś ostrzeżenia, to ignorujemy je. Jeśli wszystko przebiegnie bez komplikacji Eclipse będzie musiał uruchomić się ponownie. Po restarcie wtyczka subclipse powinien już działać. Możemy to sprawdzić klikając w Window -> Open Perspective -> Other ... i w liście perspektyw powinna znajdować się SVN Repository Exploring.