W niniejszym rozdziale:
· Analiza parametrów
· Wysyłanie poczty elektronicznej
· Stosowanie wyrażeń regularnych
· Uruchamianie programów
· Stosowanie rdzennych metod
· Występowanie jako klient RMI
· Usuwanie błędów
· Poprawa wydajności
W każdym domu znajduje się szuflada na szpargały — szuflada załadowana do pełna rzeczami, które nie do końca pasują do żadnej zorganizowanej szuflady, ale nie mogą też zostać wyrzucone, ponieważ kiedy są potrzebne, to są naprawdę potrzebne. Niniejszy rozdział pełni funkcję takiej szuflady. Zawiera spory zestaw przydatnych przykładów serwletów i porad, które nie pasują do żadnego innego miejsca. Zawarto tu serwlety, które analizują parametry, wysyłają pocztę elektroniczną, uruchamiają programy, wykorzystują mechanizmy wyrażeń regularnych i rdzenne metody oraz działają jako klienty RMI. Rozdział ten zawiera również demonstrację technik usuwania błędów, a także pewne sugestie dotyczące poprawy wydajności serwletów.
Osoby, które próbowały tworzyć własne serwlety podczas lektury niniejszej książki przypuszczalnie zauważyły, że pobieranie i analiza parametrów żądania może być dość nieprzyjemnym zajęciem, zwłaszcza jeżeli parametry te muszą zostać przekonwertowane do formatu innego niż String. Na przykład, konieczne jest odczytanie parametru count i zmiany jego wartości na format int. Poza tym, obsługa warunków błędów powinna być wykonywana przez wywołanie obslugaBezCount(), jeżeli count nie jest podany, a obslugaZlyCount(), jeżeli count nie może zostać przekształcony na liczbę całkowitą. Wykonanie tego działania przy pomocy standardowego Servlet API wymaga następującego kodu:
int count;
String param = zad.getParameter("count");
if (param=null || param.length() == 0) {
obslugaBezCount();
}
else {
try {
count = Integer.parseInt(param);
catch (NumberFormatException w) {
obslugaZlyCount();
Czy to wygląda jak porządny kod? Nie jest zbyt piękne, prawda? Lepszym rozwiązaniem jest przekazanie odpowiedzialności za pobieranie i analizę parametrów klasie narzędziowej. Klasa com.oreilly.servlet.ParameterParser jest właśnie taką klasą. Przy pomocy ParameterParser możliwe jest przepisanie powyższego kodu w bardziej elegancki sposób:
ParameterParser analiza = new ParameterParser(zad);
count = analiza.getIntParameter("count");
catch (ParameterNotFoundException w) {
Analizująca parametry metoda getIntParameter() zwraca wartość określonego parametru jako int. Zgłasza wyjątek NumberFormatException, jeżeli parametr nie może zostać przekonwertowany na int oraz ParameterNotFoundException, jeżeli parametr nie jest częścią żądania. Zgłasza również ParameterNotFoundException, jeżeli wartość parametru to pusty łańcuch. Często dzieje się tak w przypadku wysyłanych formularzy, gdy nie wpisano żadnych wiadomości do pól tekstowych, co we wszystkich przypadkach powinno być traktowane w ten sam sposób, co brakujący parametr.
Jeżeli wystarczy, aby w przypadku problemów z parametrem serwlet wykorzystywał domyślną wartość, co jest częstą praktyką, kod może zostać uproszczony w jeszcze większym stopniu:
int count = analiza.getIntParameter("count", 0);
Powyższa druga wersja getIntParameter() pobiera domyślną wartość 0, która jest zwracana zamiast zgłoszenia błędu.
Istnieje również możliwość sprawdzenia, czy w żądaniu brakuje jakichkolwiek parametrów:
String[] wymagane = { "nazwa1", "nazwa2", "konto" };
String[] brakujace = analiza.getMissingParameters(wymagane);
Powyższa metoda zwraca null, jeżeli nie brakuje żadnego parametru.
ParameterParser obsługuje także internacjonalizację poprzez metodę setCharacterEncoding(). Określa ona kodowanie, które powinno zostać wykorzystane podczas interpretacji wartości parametrów. Wartość może pochodzić z cookie użytkownika, ukrytego pola formularza lub sesji użytkownika:
analiza.setCharacterEncoding("Shift_JIS");
String wartoscJaponia = analiza.getStringParameter("nazwaLatin");
Wewnętrznie ParameterParser wykorzystuje sztuczkę getBytes() przedstawioną w rozdziale 13, „Internacjonalizacja” do obsługi konwersji. Nazwy parametrów muszą być ciągle podane w kodowaniu Latin1, ponieważ mechanizm poszukiwania wykorzystuje jeszcze nie zinternalizowane metody Servlet API getParameter() i getParameterValues().
Klasa ParameterParser zawiera ponad tuzin metod, które zwracają parametry żądania — dwie dla każdego rdzennego typu Javy. Posiada również dwie metody getStringParameter() w przypadku, gdyby konieczne było pobranie parametru w jego surowym formacie String. Kod ParameterParser jest przedstawiony w przykładzie 19.1. Wyjątek ParameterNotFoundExceptoion znajduje się w przykładzie 19.2.
Przykład 19.1.
Klasa ParameterParser
package com.oreilly.servlet;
import java.io.*;
import java.util.*;
import javax.servlet.*;
public class ParameterParser {
private ServletRequest req;
private String encoding;
public ParameterParser(ServletRequest req) {
this.req = req;
public void setCharacterEncoding(String encoding)
throws UnsupportedEncodingException {
// Sprawdzenie prawidłowości kodowania
new String("".getBytes("8859_1"), encoding);
// Jeżeli tutaj, to prawidłowe, więc jego ustawienie
this.encoding = encoding;
public String getStringParameter(String name)
throws ParameterNotFoundException {
String[] values = req.getParameterValues(name);
if (values == null) {
throw new ParameterNotFoundException(name + " not found");
else if (values[0].length() == 0) {
throw new ParameterNotFoundException(name + " was empty");
if (encoding == null) {
return values[0];
return new String(values[0].getBytes("8859_1"), encoding);
catch (UnsupportedEncodingException e) {
return values[0]; // should never happen
public String getStringParameter(String name, String def) {
try { return getStringParameter(name); }
catch (Exception e) { return def; }
public boolean getBooleanParameter(String name)
throws ParameterNotFoundException, NumberFormatException {
String value = getStringParameter(name).toLowerCase();
if ((value.equalsIgnoreCase("true")) ||
(value.equalsIgnoreCase("on")) ||
(value.equalsIgnoreCase("yes"))) {
return true;
else if ((value.equalsIgnoreCase("false")) ||
(value.equalsIgnoreCase("off")) ||
(value.equalsIgnoreCase("no"))) {
return false;
throw new NumberFormatException("Parameter " + name + " value " + value +
" is not a boolean");
public boolean getBooleanParameter(String name, boolean def) {
try { return getBooleanParameter(name); }
public byte getByteParameter(String name)
return Byte.parseByte(getStringParameter(name));
public byte getByteParameter(String name, byte def) {
try { return getByteParameter(name); }
public char getCharParameter(String name)
String param = getStringParameter(name);
if (param.length() == 0)
throw new ParameterNotFoundException(name + " is empty string");
else
return (param.charAt(0));
public char getCharParameter(String name, char def) {
try { return getCharParameter(name); }
public double getDoubleParameter(String name)
return new Double(getStringParameter(name)).doubleValue();
public double getDoubleParameter(String name, double def) {
try { return getDoubleParameter(name); }
public float getFloatParameter(String name)
return new Float(getStringParameter(name)).floatValue();
public float getFloatParameter(String name, float def) {
try { return getFloatParameter(name); }
public int getIntParameter(String name)
return Integer.parseInt(getStringParameter(name));
public int getIntParameter(String name, int def) {
try { return getIntParameter(name); }
public long getLongParameter(String name)
return Long.parseLong(getStringParameter(name));
public long getLongParameter(String name, long def) {
try { return getLongParameter(name); }
public short getShortParameter(String name)
return Short.parseShort(getStringParameter(name));
public short getShortParameter(String name, short def) {
try { return getShortParameter(name); }
public String[] getMissingParameters(String[] required) {
Vector missing = new Vector();
for (int i = 0; i < required.length; i++) {
String val = getStringParameter(required[i], null);
if (val == null) {
missing.addElement(required[i]);
if (missing.size() == 0) {
return null;
String[] ret = new String[missing.size()];
missing.copyInto(ret);
return ret;
Przykład 19.2.
Klasa ParameterNotFoundException
public class ParameterNotFoundException extends Exception {
public ParameterNotFoundException() {
super();
public ParameterNotFoundException(String s) {
super(s);
Czasami wysłanie przez serwlet wiadomości jest konieczne, a czasami jest to po prostu ułatwienie. Na przykład, proszę sobie wyobrazić serwlet otrzymujący dane z formularza użytkownika, który umieszcza tam swoje komentarze. Serwlet ten może chcieć przesłać dane formularza na listę dystrybucyjną zainteresowanych stron. Albo proszę sobie wyobrazić serwlet, który napotyka niespodziewany problem i może wysłać administratorowi pocztą stronę prosząc o pomoc.
Serwlet może wykorzystać do wysłania poczty elektronicznej jedną z czterech metod:
· Może sam zarządzać szczegółami — nawiązać połączenie przez zwykły port z serwerem poczty i wysyłając wiadomość poprzez protokół pocztowy niskiego poziomu, zazwyczaj tak zwany Simple Mail Transfer Protocol (Prosty Protokół Transferu Poczty — SMTP).
· Może uruchomić z wiersza poleceń zewnętrzny program pocztowy, jeżeli system serwera posiada taki program.
· Może wykorzystać interfejs JavaMail, zaprojektowany do pomocy w skomplikowanej obsłudze, tworzeniu i przetwarzaniu poczty (proszę zobaczyć http://java.sun.com/products/javamail).
· Może wykorzystać jedną z wielu dostępnych bezpłatnie klas pocztowych, które zmieniają szczegóły wysyłania poczty na proste i łatwe do wywołania metody.
W przypadku nieskomplikowanego wysyłania poczty poleca się ostatni sposób ze względu na jego prostotę. Dla bardziej skomplikowanych zastosowań rekomendowany jest JavaMail — zwłaszcza w przypadku serwletów działających pod kontrolą serwera J2EE, w którym na pewno dostępne są dwa pliki JAR wymagane przez JavaMail.
grzegorj