程序片實際要比NameSender.java簡單一些。這部分是由於很容易即可發出一個GET請求。此外,也不必等候回復信息。現在有兩個字段,而非一個,但大家會發現許多程序片都是熟悉的,請比較NameSender.java。
//: NameSender2.java
// An applet that sends an email address
// via a CGI GET, using Java 1.02.
import java.awt.*;
import java.applet.*;
import java.net.*;
import java.io.*;
public class NameSender2 extends Applet {
final String CGIProgram = "Listmgr2.exe";
Button send = new Button(
"Add email address to mailing list");
TextField name = new TextField(
"type your name here", 40),
email = new TextField(
"type your email address here", 40);
String str = new String();
Label l = new Label(), l2 = new Label();
int vcount = 0;
public void init() {
setLayout(new BorderLayout());
Panel p = new Panel();
p.setLayout(new GridLayout(3, 1));
p.add(name);
p.add(email);
p.add(send);
add("North", p);
Panel labels = new Panel();
labels.setLayout(new GridLayout(2, 1));
labels.add(l);
labels.add(l2);
add("Center", labels);
l.setText("Ready to send email address");
}
public boolean action (Event evt, Object arg) {
if(evt.target.equals(send)) {
l2.setText("");
// Check for errors in data:
if(name.getText().trim()
.indexOf(' ') == -1) {
l.setText(
"Please give first and last name");
l2.setText("");
return true;
}
str = email.getText().trim();
if(str.indexOf(' ') != -1) {
l.setText(
"Spaces not allowed in email name");
l2.setText("");
return true;
}
if(str.indexOf(',') != -1) {
l.setText(
"Commas not allowed in email name");
return true;
}
if(str.indexOf('@') == -1) {
l.setText("Email name must include '@'");
l2.setText("");
return true;
}
if(str.indexOf('@') == 0) {
l.setText(
"Name must preceed '@' in email name");
l2.setText("");
return true;
}
String end =
str.substring(str.indexOf('@'));
if(end.indexOf('.') == -1) {
l.setText("Portion after '@' must " +
"have an extension, such as '.com'");
l2.setText("");
return true;
}
// Build and encode the email data:
String emailData =
"name=" + URLEncoder.encode(
name.getText().trim()) +
"&email=" + URLEncoder.encode(
email.getText().trim().toLowerCase()) +
"&submit=Submit";
// Send the name using CGI's GET process:
try {
l.setText("Sending...");
URL u = new URL(
getDocumentBase(), "cgi-bin/" +
CGIProgram + "?" + emailData);
l.setText("Sent: " + email.getText());
send.setLabel("Re-send");
l2.setText(
"Waiting for reply " + ++vcount);
DataInputStream server =
new DataInputStream(u.openStream());
String line;
while((line = server.readLine()) != null)
l2.setText(line);
} catch(MalformedURLException e) {
l.setText("Bad URl");
} catch(IOException e) {
l.setText("IO Exception");
}
}
else return super.action(evt, arg);
return true;
}
} ///:~
CGI程序(不久即可看到)的名字是Listmgr2.exe。許多Web服務器都在Unix機器上運行(Linux也越來越受到青睐)。根據傳統,它們一般不為自己的可執行程序采用.exe擴展名。但在Unix操作系統中,可以把自己的程序稱呼為自己希望的任何東西。若使用的是.exe擴展名,程序毋需任何修改即可通過Unix和Win32的運行測試。
和往常一樣,程序片設置了自己的用戶界面(這次是兩個輸入字段,不是一個)。唯一顯著的區別是在action()方法內產生的。該方法的作用是對按鈕按下事件進行控制。名字檢查過以後,大家會發現下述代碼行:
String emailData =
"name=" + URLEncoder.encode(
name.getText().trim()) +
"&email=" + URLEncoder.encode(
email.getText().trim().toLowerCase()) +
"&submit=Submit";
// Send the name using CGI's GET process:
try {
l.setText("Sending...");
URL u = new URL(
getDocumentBase(), "cgi-bin/" +
CGIProgram + "?" + emailData);
l.setText("Sent: " + email.getText());
send.setLabel("Re-send");
l2.setText(
"Waiting for reply " + ++vcount);
DataInputStream server =
new DataInputStream(u.openStream());
String line;
while((line = server.readLine()) != null)
l2.setText(line);
// ...
name和email數據都是它們對應的文字框裡提取出來,而且兩端多余的空格都用trim()剔去了。為了進入列表,email名字被強制換成小寫形式,以便能夠准確地對比(防止基於大小寫形式的錯誤判斷)。來自每個字段的數據都編碼為URL形式,隨後采用與HTML頁中一樣的方式匯編GET字串(這樣一來,我們可將Java程序片與現有的任何CGI程序結合使用,以滿足常規的HTML GET請求)。
到這時,一些Java的魔力已經開始發揮作用了:如果想同任何URL連接,只需創建一個URL對象,並將地址傳遞給構建器即可。構建器會負責建立同服務器的連接(對Web服務器來說,所有連接行動都是根據作為URL使用的字串來判斷的)。就目前這種情況來說,URL指向的是當前Web站點的cgi-bin目錄(當前Web站點的基礎地址是用getDocumentBase()設定的)。一旦Web服務器在URL中看到了一個“cgi-bin”,會接著希望在它後面跟隨了cgi-bin目錄內的某個程序的名字,那是我們要運行的目標程序。程序名後面是一個問號以及CGI程序會在QUERY_STRING環境變量中查找的一個參數字串(馬上就要學到)。
我們發出任何形式的請求後,一般都會得到一個回應的HTML頁。但若使用Java的URL對象,我們可以攔截自CGI程序傳回的任何東西,只需從URL對象裡取得一個InputStream(輸入數據流)即可。這是用URL對象的openStream()方法實現,它要封裝到一個DataInputStream裡。隨後就可以讀取數據行,若readLine()返回一個null(空值),就表明CGI程序已結束了它的輸出。
我們即將看到的CGI程序返回的僅僅是一行,它是用於標志成功與否(以及失敗的具體原因)的一個字串。這一行會被捕獲並置放第二個Label字段裡,使用戶看到具體發生了什麼事情。
1. 從程序片裡顯示一個Web頁
程序亦可將CGI程序的結果作為一個Web頁顯示出來,就象它們在普通HTML模式中運行那樣。可用下述代碼做到這一點:
getAppletContext().showDocument(u);
其中,u代表URL對象。這是將我們重新定向於另一個Web頁的一個簡單例子。那個頁湊巧是一個CGI程序的輸出,但可以非常方便地進入一個原始的HTML頁,所以可以構建這個程序片,令其產生一個由密碼保護的網關,通過它進入自己Web站點的特殊部分:
//: ShowHTML.java
import java.awt.*;
import java.applet.*;
import java.net.*;
import java.io.*;
public class ShowHTML extends Applet {
static final String CGIProgram = "MyCGIProgram";
Button send = new Button("Go");
Label l = new Label();
public void init() {
add(send);
add(l);
}
public boolean action (Event evt, Object arg) {
if(evt.target.equals(send)) {
try {
// This could be an HTML page instead of
// a CGI program. Notice that this CGI
// program doesn't use arguments, but
// you can add them in the usual way.
URL u = new URL(
getDocumentBase(),
"cgi-bin/" + CGIProgram);
// Display the output of the URL using
// the Web browser, as an ordinary page:
getAppletContext().showDocument(u);
} catch(Exception e) {
l.setText(e.toString());
}
}
else return super.action(evt, arg);
return true;
}
} ///:~
URL類的最大的特點就是有效地保護了我們的安全。可以同一個Web服務器建立連接,毋需知道幕後的任何東西。