Επεξεργασία URL με Java

URL είναι ακρωνύμιο του Uniform Resource Locator και είναι η διεύθυνση ενός πόρου στον Ιστό. Παράδειγμα:

A URL with the protocol identifier and resource name labeled.

Το παράδειγμα δίνει τη διεύθυνση ενός ιστοτόπου. Το URL έχει δύο τμήματα, τα οποία χωρίζονται με "://" Στο παράδειγμα το πρωτόκολλο επικοινωνίας είναι το Hypertext Transfer Protocol (HTTP), το συνήθες πρωτόκολλο μεταφοράς υπερκειμένου. Υπάρχουν αρκετά άλλα πρωτόκολλα, όπως τα File Transfer Protocol (FTP), File και News.

Η ονομασία του πόρου εξαρτάται από το πρωτόκολλο, αλλά για τα περισσότερα διαδικτυακά πρωτόκολλα η ονομασία περιέχει τα παρακάτω στοιχεία:

Host Name Το λογικό όνομα (πχ όνομα υπηρεσίας DNS) ή η IP διεύθυνση του διακομιστή του πόρου
Port Number Η θύρα του διακομιστή όπου 'ακούει' το συγκεκριμένο πρωτόκολλο (συνήθως προκαθορισμένη).
Filename Το όνομα διαδρομής του αρχείου του πόρου στο σύστημα αρχείων του διακομιστή (σχετικό ή απόλυτο, μπορεί να περιέχει και ερώτημα προς το διακομιστή, σε περίπτωση δυναμικών σελίδων, προαιρετικό).
Reference Αναφορά σε συγκεκριμένη περιοχή (named anchor) μέσα στο αρχείο του πόρου (προαιρετικό).

Πολλές από τις πληροφορίες ονομασίας έχουν προκαθορισμένες τιμές. Για παράδειγμα, η προκαθορισμένη θύρα επικοινωνίας του πρωτοκόλλου HTTP είναι η 80. Επίσης, το προεπιλεγμένο όνομα διαδρομής για αρχείο ενός ισοτόπου είναι το /index.html.

Δύο σχετικά ακρωνύμια είναι τα URN (Universal Resource Name) και URI (Universal Resource Identification). To URN καθορίζει μόνο το μοναδικό όνομα αλλά όχι τη τοποθεσία ενός πόρου, ενώ το URL διασφαλίζει τη μοναδικότητα της ονομασίας αλλά μόνο στη συγκεκριμένη τοποθεσία και όχι γενικά. Το URI είναι μια 'ιδανική' γενίκευση των δύο ιδιοτήτων, δηλαδή η μοναδική τοποθεσία ενός πόρου με μοναδικό όνομα. 

Δημιουργία Απόλυτου URL

Έστω οτι στην εφαρμογή μας θέλουμε να προσπελάσουμε το ιστοτόπο:
http://www.gamelan.com/ 
Τότε στο πρόγραμμα Java θα ορίσουμε ένα νέο αντικείμενο URL με όρισμα ένα String:
URL gamelan = new URL("http://www.gamelan.com/");
Το αντικείμενο URL που δημιουργήθηκε είναι ένα απόλυτο (absolute) URL : περιέχει όλη την απαραίτητη πληροφορία για τη προσπέλαση του πόρου.

Δημιουργία Σχετικού URL

Ένα σχετικό (relative) URL περιέχει πληροφορία πρόσβασης ενός πόρου με βάση ένα URL που έχει ήδη προσπελαστεί. Τα σχετικά URLs είναι συνηθισμένα στην προσπέλαση πόρων που ανήκουν στον ίδιο ιστοτόπο. Για παράδειγμα έστω οτι έχετε ένα αρχείο HTML με όνομα JoesHomePage.html. Μέσα στο αρχείο υπάρχουν σύνδεσμοθ σε άλλα αρχεία, όπως το PicturesOfMe.html και MyKids.html, που βρίσκονται στον ίδιο διακομιστή και στον ίδιο κατάλογο με το αρχείο JoesHomePage.html. Οι σύνδεσμοι στα PicturesOfMe.html και MyKids.html από το JoesHomePage.html θα έχουν τη μορφή:
<a href="PicturesOfMe.html">Pictures of Me</a>
<a href="MyKids.html">Pictures of My Kids</a>
Τα URLs είναι σχετικά με το αρχείο όπου βρισκόμαστε, το JoesHomePage.html.

Έστω οτι στην εφαρμογή μας, μετά την αρχική πρόσβαση στον ιστοτόπο Gamelan θέλουμε να προσπελάσουμε τους πόρους:

http://www.gamelan.com/pages/Gamelan.game.html
http://www.gamelan.com/pages/Gamelan.net.html
Μπορούμε να δημιουργήσουμε τα εξής αντικείμενα URL σχετικά με την κοινή αρχή http://www.gamelan.com/pages/ ως εξής:
URL gamelan = new URL("http://www.gamelan.com/pages/");
URL gamelanGames = new URL(gamelan, "Gamelan.game.html");
URL gamelanNetwork = new URL(gamelan, "Gamelan.net.html");
Ο κατασκευαστής αντικειμένου URL εδώ δέχεται δύο ορίσματα: ένα βασικό αντικείμενο URL και ένα σχετικό επίθεμα σε μορφή String:
URL(URL baseURL, String relativeURL)
Αν το όρισμα baseURL είναι null, τότε ο κατασκευαστής θεωρεί το όρισμα relativeURL ως απόλυτο URL. Επίσης, αν το όρισμα relativeURL είναι απόλυτο URL, τότε ο κατασκευαστής αγνοεί το όρισμα baseURL.

Το σχετικό URL μπορεί να είναι και ένα απλό named anchor. Για παράδειγμα, έστω οτι στο αρχείο Gamelan.network.html έχει ένα named anchor με όνομα BOTTOM. Μπορούμε να ορίσμουμε ένα αντικείμενο URL ως εξής:

URL gamelanNetworkBottom = new URL(gamelanNetwork, "#BOTTOM");

Άλλοι Κατασκευαστές URL

Η κλάση URL έχει ακόμη δύο κατασκευαστές αντικειμένων URL που είναι χρήσιμοι σε περιπτώσεις που τα URLs συντίθεται από επιμέρους τμήματα, όπως host name, filename, port number, και reference που προκύπτουν από την εκτέλεση του προγράμματος.

Ο πρώτος κατασκευαστής δημιουργεί ένα αντικείμενο URL από τρία τμήματα protocol, host name, και filename. Η ακόλουθη γραμμή δημιουργεί ένα αντικείμενο URL που δείχνει στο αρχείο Gamelan.net.html στον ιστοτόπο Gamelan:

new URL("http", "www.gamelan.com", "/pages/Gamelan.net.html");
Αυτό είναι αντίστοιχο με τη γραμμή:
new URL("http://www.gamelan.com/pages/Gamelan.net.html");
Ο δεύτερος κατασκευαστής επιτρέπει και την εισαγωγή port number στα ορίσματα:
URL gamelan = new URL("http", "www.gamelan.com", 80,
"pages/Gamelan.network.html");
Το URL που κατασκευάζεται είναι τo:
http://www.gamelan.com:80/pages/Gamelan.network.html
Από το αντικείμενο URL μπορούμε να εξάγουμε το URL σε ένα String με τη μέθοδο toString ή τη μέθοδο toExternalForm.

URLs με Ειδικούς Χαρακτήρες

Μερικά URLs περιέχουν ειδικούς χαρακτήρες, όπως πχ το διάστημα:
http://foo.com/hello world/
Για να λειτουργήσει σωστά ο κατασκευαστής URL πρέπει στο String το διάστημα να κωδικοποηθεί ως '%20':
URL url = new URL("http://foo.com/hello%20world");
Στη περίπτωση που οι αλλαγές είναι αρκετές μπορούμε να χρησιμοποιήσουμε τους κατασκευαστές της κλάσης java.net.URI class που εκτελούν αυτόματα τη μετατροπή.
URI uri = new URI("http", "foo.com", "/hello world/", "");
Στη συνέχεια μετατρέπουμε το αντικείμενο URI σε αντικείμενο URL.
URL url = uri.toURL();

MalformedURLException

Αν τα ορίσματα των κατασκευαστών δεν είναι σωστά, οι κατασκευαστές URL προκαλούν εξαίρεση MalformedURLException. Τυπικά η κατασκευή μπαίνει σε δομή try/catch:
try {
URL myURL = new URL(. . .)
} catch (MalformedURLException e) {
. . .
// exception handler code here
. . .
}
Σημείωση: Τα URLs είναι αντικείμενα "write-once". Αφού δημιουργηθούν δεν μπορούν να τροποποιηθούν.

Σάρωση URL

Η κλάση URL παρέχει διάφορες μεθόδους σάρωσης αντικειμεων URL objects:
public String getProtocol()
Πρωτόκολλο του URL.
public String getAuthority()
Όνομα διακομιστή και θύρα του URL.
public String getHost()
Όνομα διακομιστή του URL.
public String getPort()
Θύρα του URL σε int, σε περίπτωση μη ορθσμού -1.
public String getPath()
Το Filename του URL, όνομα διαδρομής και ερώτημα.
public String getQuery()
Ερώτημα μέσα στο filename του URL.
public String getFile()
Όνομα δαδρομής, ίδιο με Filename αν δεν υπάρχει ερώτημα.
public String getRef()
Το named anchor του URL.

Ακολουθεί ένα παράδειγμα χρήσης των μεθόδων σάρωσης:

import java.net.*;
import java.io.*;

public class ParseURL {
public static void main(String[] args) throws Exception {
URL aURL = new URL("http://java.sun.com:80/docs/books/tutorial"
+ "/index.html?name=networking#DOWNLOADING");
System.out.println("protocol = " + aURL.getProtocol());
System.out.println("authority = " + aURL.getAuthority());
System.out.println("host = " + aURL.getHost());
System.out.println("port = " + aURL.getPort());
System.out.println("path = " + aURL.getPath());
System.out.println("query = " + aURL.getQuery());
System.out.println("filename = " + aURL.getFile());
System.out.println("ref = " + aURL.getRef());
}
}
Και το αποτέλεσμα εκτέλεσης:
protocol = http
authority = java.sun.com:80
host = java.sun.com
port = 80
path = /docs/books/tutorial/index.html
query = name=networking
filename = /docs/books/tutorial/index.html?name=networking
ref = DOWNLOADING

Ανάγνωση από URL

Μετά την επιτυχή δημιουργία ενός αντικειμένου URL, μπορούμε να καλέσουμε τη μέθοδο openStream() στο αντικείμενο URL. Η μέθοδος επιστρέφει ένα αντικείμενο ροής χαρακτήρων της κλασης java.io.InputStream όπου μπορούμε να διαβάσουμε τα περιεχόμενα του.

Το πρόγραμμα που ακολουθεί χρησιμοποιεί την openStream() για να πάρει μια ροή χαρακτήρων συνδεδεμένη με το URL http://www.yahoo.com/. Στη συνέχεια ανοίγει ένα BufferedReader στη ροή χαρακτήρων και διαβάζει από το BufferedReader ότι έρχεται από το URL. Όλα αντιγράφονται στη πρότυπη ροή εξόδου:

import java.net.*;
import java.io.*;

public class URLReader {
public static void main(String[] args) throws Exception {
URL yahoo = new URL("http://www.yahoo.com/");
BufferedReader in = new BufferedReader(
new InputStreamReader(
yahoo.openStream()));

String inputLine;

while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);

in.close();
}
}
Η εκτέλεση του προγράμματος εμφανίζει στην οθόνη τα περιεχόμενα του αρχείoυ που αντιστοιχεί στο URL που επιλέξαμε. Αν θέλουμε μπορούμε να αποθηκεύσουμε τα περιεχόμενα (με ανακατεύθυνση της εξόδου) και να τα μελετήσουμε μέσω ενός ΗTML browser.

Σύνδεση με URL

Έχοντας στη διάθεσή μας ένα αντικείμενο URL, μπορούμε να δημιουργήσουμε μια σύνδεση, δηλαδή ένα αντικείμενο URLConnection, μέσω της μεθόδου openConnection.

public URLConnection openConnection() throws IOException
Ανοίγει μια σύνδεση με το URL επιστρέφοντας ένα αντικείμενο της κλάσης URLConnection

Η αφηρημένη κλάση java.net.URLConnection, έχει αρκετές υποκλασεις, ανάλογες με τους τύπους της σύνδεσης. Για παράδειγμα:

Μέσω του αντικειμένου URLConnection μπορούμε να ρυθμίσουμε διάφορες παραμέτρους πριν ξεκινήσουμε την λειτουργία της σύνδεσης.

Η πραγματική λειτουργία ξεκινά με τη κλήση της μεθόδου URLConnection.connect. Τότε δημιουργείται μια σύνοδος μεταξύ της εφαρμογής Java που κάλεσε τη μέθοδο και του απομακρυσμένου διακομιστή που υποστηρίζει το πόρο με το συγκεκριμένο URL. Για παράδειγμα:

try {
URL yahoo = new URL("http://www.yahoo.com/");
URLConnection yahooConnection = yahoo.openConnection();
yahooConnection.connect();

} catch (MalformedURLException e) { // new URL() failed
. . .
} catch (IOException e) { // openConnection() failed
. . .


}

Η κλήση της μεθόδου connect μπορεί να γίνει και έμμεσα, μέσω της κλήσης άλλων μεθόδων που απαιτούν την ύπαρξη σύνδεσης σε λειτουργία, όπως οι getInputStream, getOutputStream, κλπ.

Ανάγνωση μέσω URLConnection

Το πρόγραμμα που ακολουθεί έχει την ίδια λειτουργία όπως αυτό που παρουσιάστηκε στην ανάγνωση από URL. Όμως τώρα η είσοδος δεν παρέχεται απ' ευθείας από το URL, αλλά μέσω του αντικειμένου URLConnection. Η λειτουργία της σύνδεσης ξεκινά έμμεσα, με τη κλήση της μεθόδου getInputStream. Στη συνέχεια, όπως και στη μέθοδο URLReader, το πρόγραμμα δημιουργεί ένα BufferedReader στη ροή εισόδου και διαβάζει από εκεί. Οι γραμμές με έντονα γράμματα επισημαίνουν τις διαφορές:
import java.net.*;
import java.io.*;

public class URLConnectionReader {
public static void main(String[] args) throws Exception {
URL yahoo = new URL("http://www.yahoo.com/");
URLConnection yc = yahoo.openConnection();
BufferedReader in = new BufferedReader(
new InputStreamReader(
yc.getInputStream()));
String inputLine;

while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
Η χρήση του URLConnection εδώ είναι περιττή, αλλά η σημασία της σύνδεσης είναι εμφανής όταν θέλουμε να χρησιμοποιήσουμε το αντικείμενο URLConnection για πιο σύνθετες ή για πολλαπλές εργασίες.

Εγγραφή μέσω URLConnection

Πολλές σελίδες HTML περιέχουν φόρμες, δηλαδή πεδία κειμένου όπου μπορούμε να εισάγουμε στοιχεία. Μετά την ειαγωγή των στοιχείων, συνήθως επιλέγουμε ένα πλαίσιο υποβολής. Σε εκείνο το σημείο, o πελάτης (συνήθως HTML browser) στέλνει τα δεδομένα που έχουμε εισάγει στη φόρμα στο URL του διακομιστή. Μετά την επιτυχή παραλαβή συνήθως υπάρχει κάποια επιβεβαίωση ή ανανέωση της σελίδας HTML.

Πολλές από τις φόρμες HTML χρησιμοποιούν την εντολή POST του πρωτοκόλλου HTTP για την αποστολή δεδομένων στο διακομιστή. Ο διακομιστής αναγνωρίζει την εντολή και παραλαμβάνει τα δεδομένα του πελάτη.

Στο παράδειγμα που ακολουθεί, στη πλευρά του πελάτη βρίσκεται ένα πρόγραμμα Java το οποίο μπορεί να παράγει δεδομένα και να τα υποβάλει στο διακομιστή. Τα βήματα είναι:

  1. Δημιουργία αντικειμένου URL του διακομιστή.
  2. Δημιουργία αντίστοιχου αντικειμένου URLConnection.
  3. Ρύθμιση δυνατότητας εγγραφής στο αντικείμενο URLConnection.
  4. Εκκίνηση της λειτουργίας σύνδεσης connect.
  5. Άνοιγμα ενός output stream στη σύνδεση.
  6. Εγγραφή στο utput stream.
  7. Κλείσιμο του output stream.

Από τη πλευρά του διακομιστή πρέπει να υπάρχει μια εφαρμογή παραλαβής των δεδομένων. Δίνονται δύο εναλλακτικές, ένα μικρό servlet με όνομα ReverseServlet ή ένα cgi-bin script με αντίστοιχη λειτουργικότητα. Και οι δύο εφαρμογές διαβάζουν τα δεδομένα του πελάτη στη μορφή string=string_to_reverse και επιστρέφουν τα δεδομένα σε αντίστροφη σειρά.

Εδώ βλέπουμε το πρόγραμμα του πελάτη: 

import java.io.*;
import java.net.*;

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

if (args.length != 2) {
System.err.println("Usage: java Reverse " +
"http://<location of your servlet/script>" +
" string_to_reverse");
System.exit(1);
}

String stringToReverse = URLEncoder.encode(args[1], "UTF-8");

URL url = new URL(args[0]);
URLConnection connection = url.openConnection();
connection.setDoOutput(true);

OutputStreamWriter out = new OutputStreamWriter(
connection.getOutputStream());
out.write("string=" + stringToReverse);
out.close();

BufferedReader in = new BufferedReader(
new InputStreamReader(
connection.getInputStream()));

String decodedString;

while ((decodedString = in.readLine()) != null) {
System.out.println(decodedString);
}
in.close();
}
}
Κατ' αρχήν το πρόγραμμα δέχεται δύο ορίσματα στη γραμμή εντολών. Το URL της εφαρμογής διακομιστή και το string που θα αντιστραφεί:
if (args.length != 2) {
System.err.println("Usage: java Reverse " +
"http://<location of your servlet/script>" +
" string_to_reverse");
System.exit(1);
}

String stringToReverse = URLEncoder.encode(args[1], "UTF-8");
Το URL πρέπει να ακολουθεί τις συμβάσεις που ήδη περιγράψαμε. Το string πρέπει να κωδικοποιηθεί πριν την αποστολή του, ώστε να μη περιέχει διαστήματα ή άλλους μη-αλφαριθμητικούς χαρακτήρες. Η κλάση URLEncoder παρέχει κατάλληές μεθόδους.

Στη συνέχεια, το πρόγραμμα δημιουργεί το αντικείμενο URL ,τη σύνδεση και την ρυθμίζει για εγγραφή:

URL url = new URL(args[0]);
URLConnection connection = url.openConnection();
connection.setDoOutput(true);
Το πρόγραμμα δημιουργεί ένα output stream στη σύνδεση και ανοίγει ένα αντικείμενο OutputStreamWriter:
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
Αν το URL δεν υποστηρίζει εγγραφή, η μέθοδος getOutputStream παράγει εξαίρεση UnknownServiceException.

Κατόπιν το πρόγραμμα γράφει τα δεδομένα και κλείνει το stream:

out.write("string=" + stringToReverse);
out.close();
Σε αυτό το σημείο το πρόγραμμα αναμένει την απόκριση της εφαρμογής του διακομιστή. Η εφαρμογή αντιστρέφει το string και το στέλνει πίσω στο πρόγραμμα του πελάτη. Η συνέχεια του προγράμματος Reverse είναι η εξής:
BufferedReader in = new BufferedReader(
new InputStreamReader(
connection.getInputStream()));

String decodedString;

while ((decodedString = in.readLine()) != null) {
System.out.println(decodedString);
}
in.close();
Αν η εφαρμογή διακομιστή ReverseServlet βρίσκεται στο URL http://foobar.com/servlet/ReverseServlet, τότε η εκτέλεση του προγράμματος Reverse θα πρέπει να έχει ορίσματα γραμμής εντολών τα
http://foobar.com/servlet/ReverseServlet "Reverse Me"
Το αποτέλεσμα της εκτέλεσης θα είναι κάπως έτσι:
Reverse Me
reversed is:
eM esreveR

Το πρόγραμμα του ReverseServlet του διακομιστή.


import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.net.*;

public class ReverseServlet extends HttpServlet
{
private static String message = "Error during Servlet processing";

public void doPost(HttpServletRequest req, HttpServletResponse resp) {
try {
int len = req.getContentLength();
byte[] input = new byte[len];

ServletInputStream sin = req.getInputStream();
int c, count = 0 ;
while ((c = sin.read(input, count, input.length-count)) != -1) {
count +=c;
}
sin.close();

String inString = new String(input);
int index = inString.indexOf("=");
if (index == -1) {
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
resp.getWriter().print(message);
resp.getWriter().close();
return;
}
String value = inString.substring(index + 1);

//decode application/x-www-form-urlencoded string
String decodedString = URLDecoder.decode(value, "UTF-8");

//reverse the String
String reverseStr = (new StringBuffer(decodedString)).reverse().toString();

// set the response code and write the response data
resp.setStatus(HttpServletResponse.SC_OK);
OutputStreamWriter writer = new OutputStreamWriter(resp.getOutputStream());

writer.write(reverseStr);
writer.flush();
writer.close();
} catch (IOException e) {
try{
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
resp.getWriter().print(e.getMessage());
resp.getWriter().close();
} catch (IOException ioe) {
}
}

}

}


Το εναλλακτικό cgi-script του διακομιστή:

#!/usr/bin/perl
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);
foreach $pair (@pairs)
{
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
# Stop people from using subshells to execute commands
$value =~ s/~!/ ~!/g;
$FORM{$name} = $value;
}

print "Content-type: text/plain\n\n";
print "$FORM{'string'} reversed is: ";
$foo=reverse($FORM{'string'});
print "$foo\n";
exit 0;

Μεταφόρτωση αρχείων μέσω URLConnection

Το παρακάτω παράδειγμα δείχνει τη μεταφόρτωση αρχείων από και προς ένα διακομιστή FTP.
import java.io.*;
import java.net.*;
 
/**
 * This class is used to upload a file to a FTP server.
 *
 * @author Muthu
 */
public class FileUpload
{
 
   /**
    * Upload a file to a FTP server. A FTP URL is generated with the
    * following syntax:
    * ftp://user:password@host:port/filePath;type=i.
    *
    * @param ftpServer , FTP server address (optional port ':portNumber').
    * @param user , Optional user name to login.
    * @param password , Optional password for user.
    * @param fileName , Destination file name on FTP server (with optional
    *            preceding relative path, e.g. "myDir/myFile.txt").
    * @param source , Source file to upload.
    * @throws MalformedURLException, IOException on error.
    */
   public void upload( String ftpServer, String user, String password,
         String fileName, File source ) throws MalformedURLException,
         IOException
   {
      if (ftpServer != null && fileName != null && source != null)
      {
         StringBuffer sb = new StringBuffer( "ftp://" );
         // check for authentication else assume its anonymous access.
         if (user != null && password != null)
         {
            sb.append( user );
            sb.append( ':' );
            sb.append( password );
            sb.append( '@' );
         }
         sb.append( ftpServer );
         sb.append( '/' );
         sb.append( fileName );
         /*
          * type ==&gt; a=ASCII mode, i=image (binary) mode, d= file directory
          * listing
          */
         sb.append( ";type=i" );
 
         BufferedInputStream bis = null;
         BufferedOutputStream bos = null;
         try
         {
            URL url = new URL( sb.toString() );
            URLConnection urlc = url.openConnection();
 
            bos = new BufferedOutputStream( urlc.getOutputStream() );
            bis = new BufferedInputStream( new FileInputStream( source ) );
 
            int i;
            // read byte by byte until end of stream
            while ((i = bis.read()) != -1)
            {
               bos.write( i );
            }
         }
         finally
         {
            if (bis != null)
               try
               {
                  bis.close();
               }
               catch (IOException ioe)
               {
                  ioe.printStackTrace();
               }
            if (bos != null)
               try
               {
                  bos.close();
               }
               catch (IOException ioe)
               {
                  ioe.printStackTrace();
               }
         }
      }
      else
      {
         System.out.println( "Input not available." );
      }
   }
 
   /**
    * Download a file from a FTP server. A FTP URL is generated with the
    * following syntax:
    * ftp://user:password@host:port/filePath;type=i.
    *
    * @param ftpServer , FTP server address (optional port ':portNumber').
    * @param user , Optional user name to login.
    * @param password , Optional password for user.
    * @param fileName , Name of file to download (with optional preceeding
    *            relative path, e.g. one/two/three.txt).
    * @param destination , Destination file to save.
    * @throws MalformedURLException, IOException on error.
    */
   public void download( String ftpServer, String user, String password,
         String fileName, File destination ) throws MalformedURLException,
         IOException
   {
      if (ftpServer != null && fileName != null && destination != null)
      {
         StringBuffer sb = new StringBuffer( "ftp://" );
         // check for authentication else assume its anonymous access.
         if (user != null && password != null)
         {
            sb.append( user );
            sb.append( ':' );
            sb.append( password );
            sb.append( '@' );
         }
         sb.append( ftpServer );
         sb.append( '/' );
         sb.append( fileName );
         /*
          * type ==&gt; a=ASCII mode, i=image (binary) mode, d= file directory
          * listing
          */
         sb.append( ";type=i" );
         BufferedInputStream bis = null;
         BufferedOutputStream bos = null;
         try
         {
            URL url = new URL( sb.toString() );
            URLConnection urlc = url.openConnection();
 
            bis = new BufferedInputStream( urlc.getInputStream() );
            bos = new BufferedOutputStream( new FileOutputStream(
                  destination.getName() ) );
 
            int i;
            while ((i = bis.read()) != -1)
            {
               bos.write( i );
            }
         }
         finally
         {
            if (bis != null)
               try
               {
                  bis.close();
               }
               catch (IOException ioe)
               {
                  ioe.printStackTrace();
               }
            if (bos != null)
               try
               {
                  bos.close();
               }
               catch (IOException ioe)
               {
                  ioe.printStackTrace();
               }
         }
      }
      else
      {
         System.out.println( "Input not available" );
      }
   }
}