Zurück

Akadia Information Technology


Drag and drop of tree nodes in Java/Swing
New printing in Java 2
Text position and height in Java
Reading clipboard content with Java
Save / restore file to / from DB using JAVA and JDBC
Reading non plain ASCII from HTML into a Java Servlet
Stored Procedures with IN and OUT Paramerters
Sorting Arrays with Java
Reading non plain ASCII from HTML Forms
File Upload with Java using Oracle Application Server
Java Exceptions by Example
Java Pitfall: the lost exception


Drag and drop of tree nodes in Java/Swing

These code snippets show how to move Swing JTree nodes with the mouse by the use of drag and drop.

Two event listeners need to be installed. First a mouse motion listener to indicate the drag operation and second a mouse listener to catch the mouse released event at the time of drop.

The mouse motion listeners mouseDragged() method is invoked as long as the mouse key remains pressed and reports all the positions along the way the mouse is moved. This offers the possibility to add actions during drag operation. We could think about to show a special cursor symbol over areas where the drop of our node is prohibited. In our sample we don't do this and introduce a flag to indicate if the method mouseDragged() is invoked the first time or not. We don't want to do to many activities every time the mouse position is changing:

private boolean _treeDragging = false;

First see the drag operation. At the end the method mouseDragOnTree() is invoked that is described later.

_tree.addMouseMotionListener( new MouseMotionAdapter() {

  public void mouseDragged(MouseEvent e) {

Enter here only one time after drag operation started:

    if (!_treeDragging) {
      _treeDragging = true;

Set cursor:

      setCursor(new Cursor(Cursor.HAND_CURSOR));

Read node which is dragged:

      TreePath path = _tree.getPathForLocation(e.getX(), e.getY());
      TreeNode node = null;
      if (path != null)
        node = (TreeNode) path.getLastPathComponent();
      mouseDragOnTree(e, node);
    }
  }
});

Now the drop operation. The variable node gets the node on which the dragged one has been dropped. At the end the method mouseDropOnTree() is invoked described later.

_tree.addMouseListener( new MouseAdapter() {

  public void mouseReleased(MouseEvent e) {
    if (_treeDragging) {
       TreePath path = _tree.getPathForLocation(e.getX(), e.getY());
       _treeDragging = false;
       setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
       TreeNode node = null;
       if (path != null)
         node = (TreeNode) path.getLastPathComponent();
       mouseDropOnTree(e, node);
     }
  }
});

The following method stores the dragged tree node in _treeDraggingNode and locks the tree during drag and drop operation.

private TreeNode _treeDraggingNode = null;

protected void mouseDragOnTree(MouseEvent e, TreeNode node) {
  if (node != null) {
    _treeDraggingNode = node;

    // Lock tree
    _tree.setEditable(false);
  }
}

The drop operation is more complicated. See explanations after this code sample:

protected void mouseDropOnTree(MouseEvent e, TreeNode node) {

  if (node != null && _treeDraggingNode != null) {

    TreeNode target = node;

    if (_treeDraggingNode != target) {

      // Store to be moved nodes parent
      TreeNode draggingTreeNodeParent =
        (TreeNode)_treeDraggingNode.getParent();

      if (draggingTreeNodeParent != null) {

        // According Java documentation no remove before
        // insert is necessary. Insert removes a node if already
        // part of the tree. However the UI does not refresh
        // properly without remove

        _treeModel.removeNodeFromParent(_treeDraggingNode);

        // If node to be moved is a target's ancestor
        // To be moved nodes child leading to the target
        // node must be remounted and added to the to be moved
        // nodes parent

        TreeNode prevAncestor = null;
        TreeNode ancestor     = target;
        boolean  found = false;
        do {
          if (ancestor == _treeDraggingNode) {
            found = true;
            break;
          }
          prevAncestor = ancestor;
          ancestor     = ancestor.getParent();
        }
        while (ancestor != null);
        if (found && prevAncestor != null)
          _treeModel.insertNodeInto((TreeNode) prevAncestor,
          draggingTreeNodeParent, 0);

        // Now insert to be moved node at its new location
        _treeModel.insertNodeInto(_treeDraggingNode, target, 0);
        _treeDraggingNode = null;
      }
    }
  }

  // Unlock tree
  _tree.setEditable(true);

}

The usual situation looks like this:

Drag on node (2) and drop on node (3). (2) appears now below (3). All nodes below (2) are moving together with (2) to the new place.

A more special case: Again drag on node (2) and drop on node (3). But this time (3) is part of the nodes below (2). We cannot move all nodes below (2)! (3) and all descendants must remain.

This is how drag and drop of tree nodes can be implemented. There may be your own program covering your requirements. You may use this samples as starting point for a quick and easy implementation.

New printing in Java 2

A new concept for printing has been introduced in JDK 1.2 (Java 2) after changes in the java.awt.PrintJob way of printing resulted sometimes in huge amount of data sent to the printer for every page. This made the life not easy.

The concept to start a page, draw to it and finish (dispose) it does not exist anymore. Instead a method is invoked by the print manager with the current page number as parameter. For every method call you must draw all items belonging to the requested page.

The new classes can be found in: 

import java.awt.print.*;

Start a print job

This is a small code snipped to show the start of a print job. First of all a print job has to be created with PrinterJob.getPrinterJob(). The default page can be referenced and also modified afterwards. In our sample we change the paper orientation to landscape.
Before displaying a print dialog printerJob.setPrintable(this, pageFormat) assigns an object to be printed and adds our page format. What a printable object is will be answered below. Finally we show the print dialog with printerJob.printDialog(). If this method returns true, the user pressed the ok button and we can start printing.

PrinterJob printerJob = PrinterJob.getPrinterJob();

PageFormat pageFormat = printerJob.defaultPage();
pageFormat.setOrientation(PageFormat.LANDSCAPE);

printerJob.setPrintable(this, pageFormat);
printerJob.setJobName("job name");

if (printerJob.printDialog()) {
    printerJob.print();
}

Why not showing a page dialog to the user where he can set sizes and borders? This can be done by replacing the line printerJob.setPrintable(this, pageFormat); in the above sample:

PageFormat userFormat = printerJob.pageDialog(pageFormat);
printerJob.setPrintable(this, userFormat);

Print pages

This is now a sample to show what a printable object is. A printable object is an object which implements the Printable interface.

public class SampleClass implements Printable { ...

The interface requires a print(Graphics, PageFormat, int) method. The last parameter is the zero based pageIndex. A PrinterJob calls the Printable objects print() method to request the page specified by pageIndex.

Its now your turn to draw and write within this print() method all items belonging to the page specified by pageIndex. You may use methods like drawLine() and drawString() to send your items to the printer. If you printed all your data but the print() method is still called, return NO_SUCH_PAGE. If you sent data to the printer return PAGE_EXISTS.

public int print(Graphics pg, PageFormat pageFormat, int pageIndex) 
            throws PrinterException {

    if (pageIndex >= maxNumPages)
        return NO_SUCH_PAGE;

    pg.drawLine(...);
    pg.drawString(...);

    return PAGE_EXISTS;

Unfortunately this method may be invoked many times with the same pageIndex. You have to send every time the same data to the printer.

Handle errors

If the Printable object runs into an error the print job throws a PrinterException.

You may catch the following two sub classes of PrinterException:

  • PrinterAbortException: Print job cancelled by application or user

  • PrinterIOException: Printer has an error like paper out, wrong printer settings (offline) or connection problems

Text position and height in Java

If you have problems to position your text for instance on screen or printing panels one of these tips may help you:

  • Use Graphics.getFontMetrics(Font) to measure text. This method returns a FontMetrics object. The real font height depends on the current graphics context. To use for example Font's getSize() method is not necessarily correct.
  • Texts are not positioned according the upper left corner like all other objects. Instead the leftmost point on the baseline is used.
  • Graphics.getFontMetrics(Font).getHeight() does not return the font height. See explanation and sketch below.

The so-called height is the distance between two lines of text. To measure the real font height (lets call it font size) use FontMetrics's methods getAscent() + getDescent() together.

Both classes Graphics and FontMetrics can be found in /java/awt

Reading clipboard content with Java

There may be situations where you like to read the clipboard content in Microsoft Windows or any other operating system as a string for debug reasons. You like to know exactly what is stored there to used it in your own program or to check if the string you sent to the clipboard is correct.

This is how easy it works...

First of all you need the following imports if you want to use the code snippet below in your own program:

import java.awt.*;
import java.awt.datatransfer.*;
import java.lang.*;

Get a reference to the clipboard:

Clipboard clipboard = getToolkit().getSystemClipboard();

Catch a transferable content object. If the clipboard is empty or does not contain a character-based object, this method returns null:

Transferable content = clipboard.getContents(this);

if (content != null) {

    try {

Read it in a string flavour:

        String clipboardData = (String)
        content.getTransferData(DataFlavor.stringFlavor);

...and here's the content:


        for (int i = 0; i < clipboardData.length(); i++) {

            if (clipboardData.charAt(i) == '\r')
                System.out.println("<CR>");
            else if (clipboardData.charAt(i) == '\n')
                System.out.println("<LF>");
            else if (clipboardData.charAt(i) == ' ')
                System.out.print("< >");
            else
                System.out.print(clipboardData.charAt(i));
        }

    }
    catch (Exception ex) {
        ex.printStackTrace();
        getToolkit().beep();
    }
}

  Save / restore file to / from database using JAVA and JDBC

In this short tip, we want to present how to write data from a file to the an Oracle table using Java and JDBC. Then we want to restore this file from the database to the filesystem again. The source code have been reduced to the minimum to simplify the understanding of the used mechanisms. Checks like "file exists", "row exists" have been removed. We used the thin JDBC driver for this demonstration.

Used Environment

  • Oracle 8.1.6 RDBMS (SUN Solaris)
  • JDK 1.1.8 (JDeveloper 3.0)
  • JDBC included in Oracle 8.1.6
  • CLASSPATH = $ORACLE_HOME/jdbc/lib/classes111.zip

Created Test Table

CREATE TABLE image (
  id    NUMBER(8),
  image LONG ROW
);

Write data from the file to the database

import java.sql.*;
import java.io.*;

// Write data from a file (binary) to a long raw via JDBC
// These source code have been reduced to the minimum to
// simplify the understanding of the used mechanisms. Checks
// like "file exists", "row exists" have been removed

class file2db
{
  public static void main (String args [])
  throws SQLException, ClassNotFoundException
  {
    // Load the Oracle JDBC driver
    Class.forName ("oracle.jdbc.driver.OracleDriver");

    // Connect to the database
    Connection conn = DriverManager.getConnection
    ("jdbc:oracle:thin:@quorum:1523:SOL3","scott","tiger");

    // Read arguments, first argument = row number,
    // second argument = file to read.

    String row = args[0];
    String filename = args[1];

    // Open the file
    File file = new File(filename);

    // Get file size
    int filesize= (int) file.length();
    System.out.println("Inserting "+filename+"
    ("+filesize+" Bytes) into the table IMAGE at row: "+row);

    try {

     // Read file content into a data stream
      InputStream fin = new FileInputStream(file);

      // Prepare SQL Statement
      PreparedStatement pstmt = conn.prepareStatement
      ("INSERT INTO image VALUES("+row+",?)");
      pstmt.setBinaryStream(1,fin,filesize);

      // Execute SQL Statement
      pstmt.executeUpdate();

      // Cleanup
      fin.close();

    } catch (IOException e) {
      System.out.println("Err: "+e);
    }
  }
}

Compile the Java Code using the java compiler

$ javac file2db

Now, write a text or binary file from the filesystem to the database.

$ java file2db 1 myfile

Inserting myfilet (991 Bytes) into the table IMAGE at row: 1

Restore data from the database in a file

import java.sql.*;
import java.io.*;

// Restore data from the database (long raw) to a file via JDBC.
// These source code have been reduced to the minimum to
// simplify the understanding of the used mechanisms. Checks
// like "file exists", "row exists" have been removed
class db2file
{
  public static void main (String args [])
  throws SQLException, ClassNotFoundException
  {
    // Load the Oracle JDBC driver
    Class.forName ("oracle.jdbc.driver.OracleDriver");

    // Connect to the database
    Connection conn = DriverManager.getConnection
    ("jdbc:oracle:thin:@quorum:1523:SOL3","scott","tiger");

    // Read arguments, first argument = row number,
    // second argument = file to read.

    String row = args[0];
    String filename = args[1];

    System.out.println
    ("Get "+filename+" from the table IMAGE at row: "+row);

    try {

      // Open buffer (ResultSet) for the file
      Statement stmt = conn.createStatement();
      ResultSet rset = stmt.executeQuery
      ("SELECT image FROM image WHERE id = "+row);

      // Fetch file content in buffer
      if (rset.next()) {
        FileOutputStream file = new FileOutputStream(filename);
        InputStream fin = rset.getBinaryStream(1);

        // Loop through buffer until EOF and write file to disc
        int c;
        while ((c = fin.read()) != -1) {
          file.write(c);
        }
        fin.close();
      }
    } catch (IOException e) {
      System.out.println("Err: "+e);
    }
  }
}

Compile the Java Code using the java compiler

$ javac db2file

Now, restore text or binary file from the database back to the filesystem.

$ java db2file 1 myfile_copy

Get myfile_copy from the table IMAGE at row: 1

Download

Download file2db.java
Download db2file.java

Reading non plain ASCII from HTML into a Java Servlet

When you want to read extended characters (e.g. ISO8859P1: ü,ö,ä) from a HTML Form into a Java Servlet, make sure you set the correct Characterset, before reading the values using getURLParameter(), or you will get only 7-Bit characters.

// Get HTML-Form Parameters

import java.util.*;
import oracle.html.*;
import oracle.owas.wrb.services.http.HTTP;

public class GetFormParm {

  protected HTTP http = null;

  // Constructor
  public GetFormParm() {
    this.http = HTTP.getRequest();
  }

  // Get URL Parameter for strKey
  public static String getURLParm(HTTP http, String strKey) {
    String strValue;

    // Set URL character set (default ISO-8859-1)
    http.setURLCharacterSet("ISO-8859-1");
    strValue = http.getURLParameter(strKey);
    if (strValue == null) { strValue = ""; }
    if (strValue.compareTo("") == 0) { strValue = ""; }

    return strValue;
  }
}

Stored Procedures with IN and OUT Paramerters using JDBC

Christoph Gächter, Akadia AG

JDBC provides a stored procedure SQL escape that allows stored procedures to be called in a standard way for all RDBMs. This escape syntax has one with and one without result parameter. If the result parameter is used, it must be registered as an OUT parameter. The other parameters can be used for input, output or both. All parameters are referred to sequentially, by number, beginning by 1.

begin ? := func [(?,?,?...)]; end;
begin proc [(?,?,?...)]; end;

According to the SQL92 syntax:

{?= call func [(?,?,?...)]}
{call proc [(?,?,?...)]}

The class CallableStatement inherits the class PreparedStatement and this class is the successor of Statement. As the names CallableStatement says, in this class extensions for IN and OUT parameter are defined. IN parameter values are set using the set methods inherited from PreparedStatement. OUT parameter must be registered prior to executing the stored procedure. Their values are retrieved after execution via the predefined getXXX() methods.

A CallableStatement can return one value, one ResultSet or multiple ResultSet objects. Some examples show you the usage of the class CallableStatement.

Simple Stored Procedure

The PL/SQL package java_demo defines the procedure get_emp(), using the IN parameter p_id and the OUT parameters p_ename, p_job, p_sal, analogous to the attributes of the table EMP (schema scott/tiger).

import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;

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

    // You can put a database name after
    // the @ sign in the connection URL.

    String strUrl = "jdbc:oracle:oci8:@asu1";
    Connection dbConn = null;

    // Register Oracle driver
    Class.forName("oracle.jdbc.driver.OracleDriver");

    // Connect to the database
    dbConn = DriverManager.getConnection(strUrl,"scott","tiger");

    // Prepare a PL/SQL call
    // PROCEDURE get_emp(p_id IN NUMBER,
    //                    p_ename OUT VARCHAR2,
    //                    p_job OUT VARCHAR2,
    //                    p_sal OUT NUMBER);

    String strQuery = "{call java_demo.get_emp(?, ?, ?, ?)}";
    CallableStatement cstmt = dbConn.prepareCall(strQuery);

    // The id argument is the first ?
    // Declare that the second ? is a return value of type VARCHAR
    // Declare that the third ? is a return value of type VARCHAR
    // Declare that the forth ? is a return value of type NUMBER

    cstmt.setInt(1, 7654);
    cstmt.registerOutParameter(2, OracleTypes.VARCHAR);
    cstmt.registerOutParameter(3, OracleTypes.VARCHAR);
    cstmt.registerOutParameter(4, OracleTypes.NUMBER);
    cstmt.execute();

    // Get the data
    System.out.println(cstmt.getString(2));
    System.out.println(cstmt.getString(3));
    System.out.println(cstmt.getDouble(4));

    // Close statement and connection
    cstmt.close();
    dbConn.close();
  }
}

Simple Stored Function

The PL/SQL package java_demo defines the function get_sal(), using the IN parameter p_id and the result parameter sal, analogous to the attributes of the table EMP (schema scott/tiger).

    ...

    // Prepare a PL/SQL call
    // FUNCTION get_sal(p_id IN NUMBER) RETURN NUMBER;

    String strQuery = "{ ? = call java_demo.get_sal(?)}";
    CallableStatement cstmt = dbConn.prepareCall(strQuery);

    // Declare that the first ? is a return value of type DOUBLE
    // The id argument is the second ?

    cstmt.registerOutParameter(1, OracleTypes.NUMBER);
    cstmt.setInt(2, 7654);
    cstmt.execute();

    // Get the data
    System.out.println(cstmt.getDouble(1));

    ...

Stored Function with Record Set

The PL/SQL package java_demo defines the procedure list_emp(), using the IN parameter p_job and the result parameter as ResultSet, analogous to the attributes of the table EMP (schema scott/tiger). The ResultSet contains all the employees matching the defined job.

    ...

   // Prepare a PL/SQL call
    // FUNCTION list_emp(p_job VARCHAR2) RETURN myreftype;

    String strQuery = "{ ? = call java_demo.list_emp(?)}";
    CallableStatement cstmt = dbConn.prepareCall(strQuery);

    // Select all the SALESMAN person
    // Declare that the first ? is a return value
    // of type OBJECT (Cursor Ref.)

    cstmt.registerOutParameter(1, OracleTypes.CURSOR);
    cstmt.setString(2, "SALESMAN");
    cstmt.execute();
    ResultSet rset = (ResultSet)cstmt.getObject(1);

    // Loop the cursor
    while (rset.next()) {
      System.out.println(rset.getString("ename"));
    }

    ...

This example shows the easy and smart way how to open and to use a cursor defined in
the PL/SQL package. Important in this case is the definition of the public cursor type:

TYPE myreftype IS REF CURSOR RETURN emp%ROWTYPE;
FUNCTION list_emp(p_job VARCHAR2) RETURN myreftype;

Sorting Arrays with Java

Christoph Gächter, Akadia AG

Hier werden einige Möglichkeiten gezeigt, wie mit Java Arrays sortiert werden können . (Siehe dazu auch JDC Tech Tips vom 23. September 1999). Voraussetzung für die folgenden Code-Beispiele ist Java 2 SDK, 1.2.x.

Primitive Typen

Gemeint sind hier die Java Primitive Data Types und weniger unliebsame Zeitgenossen. Dazu gehören byte, char, double, float, int, long und short. Diese können äusserst einfach sortiert werden, indem die Methode sort() der Klasse Arrays aufgerufen wird:

public class SortPrimitive {

  // Sorts an array of double values
  public static void main(String[] args) {
    double[] dblAry = new double[10];

    for (int i = 0; i < dblAry.length; i++) {
      dblAry[i] = Math.random();
    }

    // Sort the array
    Arrays.sort(dblAry);

    // Print the array
    for (int i = 0; i < dblAry.length; i++) {
      System.out.println(dblAry[i]);
    }
  }
}

Interface Comparable

Objekte, welche das Interface Comparable implementieren, lassen sich ebenso einfach sortieren. Zu diesen Objekten gehört die Klasse String, wie folgendes Beispiel zeigt:

import java.util.*;
import java.awt.*;

public class SortString {

  // Sorts an array of strings
  public static void main(String[] args) {
    String[] strAry = new String[4];
    strAry[0] = "abc";
    strAry[1] = "bcd";
    strAry[2] = "Abc";
    strAry[3] = "bCd";

    // Sort the array
    Arrays.sort(strAry);

    // Print the array
    for (int i = 0; i < strAry.length; i++) {
      System.out.println(strAry[i]);
    }
  }
}

Was aber, wenn die zu sortierende Klasse das Interface Comparable nicht implementiert? Hier gibt es zwei Möglichkeiten: Die erwähnte Klasse kann vererbt werden und gleichzeitig das Interface implementieren oder es wird eine Vergleichsklasse (Interface Comparator) implementiert.

Sehen wir uns zuerst die Möglichkeit an, die Klasse zu vererben: Das Interface Comparable erfordert die Implementation der Methode compareTo(). Diese ermittelt anhand des übergebenen Objektes die Werte 1 (grösser), 0 (gleich gross) bzw. –1 (kleiner):

import java.util.*;
import java.awt.*;

public class MyPoint extends Point implements Comparable {

  MyPoint(int x, int y) {
    super(x, y);
  }

  public int compareTo(Object o) {
    MyPoint p = (MyPoint) o;
    double d1 = Math.sqrt(x * x + y * y);
    double d2 = Math.sqrt(p.x * p.x + p.y * p.y);

    if (d1 < d2) {
      return -1;
    } else if (d2 < d1) {
      return 1;
    } else {
      return 0;
    }
  }
}

Wiederum erfolgt die Sortierung äusserst einfach:

import java.util.*;
import java.awt.*;

public class SortPoint {

  // Sorts an array of points
  public static void main(String[] args) {
    Random rnd = new Random();
    MyPoint[] pntAry = new MyPoint[10];

    for (int i = 0; i < pntAry.length; i++) {
      pntAry[i] = new MyPoint(rnd.nextInt(100), rnd.nextInt(100));
    }

    // Sort the array
    Arrays.sort(pntAry);

    // Print the array
    for (int i = 0; i < pntAry.length; i++) {
      System.out.println(pntAry[i]);
    }
  }
}

Interface Comparator

Falls es nicht möglich ist, die Klasse zu vererben, kann eine eigenständige Vergleichsklasse implementiert werden. Diese implementiert ihrerseits das Interface Comparator.

Dieses Interface erfordert die Umsetzung der beiden Methoden compareTo() und equals(). Die Methode compareTo() kennen wir bereits aus vorherigem Kapitel, die Methode equals() gibt true zurück, falls die beiden Objekte gleich gross sind.

Hier als Beispiel ein Comparator für die Klasse Points:

import java.util.*;
import java.awt.*;

public class PointComparator implements Comparator {

  public int compare(Object o1, Object o2) {
    Point p1 = (Point) o1;
    Point p2 = (Point) o2;
    double d1 = Math.sqrt(p1.x * p1.x + p1.y * p1.y);
    double d2 = Math.sqrt(p2.x * p2.x + p2.y * p2.y);

    if (d1 < d2) {
      return -1;
    } else if (d2 < d1) {
      return 1;
    } else {
      return 0;
    }
  }

  public boolean equals(Object o1, Object o2) {
    return compare(o1, o2) == 0;
  }
}

Die Sortierung des Arrays erfolgt wiederum mit der Methode sort(), diesmal wird nebst dem zu sortierenden Array auch noch der Comparator mitgegeben:

import java.util.*;
import java.awt.*;

public class SortByComparator {

  // Sorts an array of points
  public static void main(String[] args) {
    Random rnd = new Random();
    Point[] pntAry = new Point[10];

    for (int i = 0; i < pntAry.length; i++) {
      pntAry[i] = new Point(rnd.nextInt(100), rnd.nextInt(100));
    }

    // Sort the array
    Arrays.sort(pntAry, new PointComparator());

   // Print the array
    for (int i = 0; i < pntAry.length; i++) {
      System.out.println(pntAry[i]);
    }
  }
}

Fazit

Java stellt einige gute Möglichkeiten zur Verfügung, beliebige Objekte in einem Array zu sortieren, ohne dass sich der Entwickler um die Details kümmern muss. Interessant erscheint uns auch die Möglichkeit, eigene Sortierkriterien zu implementieren unter Verwendung eines speziellen Comparators (z.B. Unterdrückung Gross-/ Kleinschreibung, Buchstaben vor Zahlen usw.).

File Upload with Java using Oracle Application Server OAS

Matthias Ehrsam, Sohard AG

This Tip shows how to implement uploading a file from a HTTP client (Browser must support the File Input Tag) to the HTTP server. This is often used in intranet's to allow clients, which must usually first authorize on the server, to upload files from the local machine to a central directory.

The following HTML form shows a possible user interface. The user can browse the local filesystem with the Browse button.

file_upload.gif (3121 bytes)

Use the following HTML Code

<html>
<head>
  <title>File Upload</title>
</head>
<h1>File Upload</h1>
<br>
<form action="/upload/FileUploadDo"
  enctype="multipart/form-data" method="post">
  <input type="file" name="upfile" size="60"><br><br>
  <input type="submit" value="Start Upload">
  <input type="reset" value="Reset">
</form>
</html>

Use the following Java Code for OAS

import oracle.owas.wrb.services.http.*;
import oracle.html.*;
import java.io.*;
import java.util.*;

// Class to Upload a file using Oracle Application Server OAS
public class FileUploadDo {

  public static final String WRITE_PATH = "<PathToUploadDir>";

  byte b[] = null;
  String content_disposition;
  String content_type;
  String _filename = null;
  int boundary_length=0;
  int _start = 0;
  int amount = 0;

  // Entry Point Method, called by main()
  public FileUploadDo() {
    getFileData();
    getHeaderInformation();
    writeFile();
    response(_filename);
  }

  // Get File Information from HTTP client
  private void getFileData() {
    HTTP request = HTTP.getRequest();
    b = request.getURLParameterContent();
  }

  // Read File from HTTP client
  private void getHeaderInformation(){
    BufferedReader br;
    br = new BufferedReader(new InputStreamReader(
             new ByteArrayInputStream(b)));
    try {
      boundary_length = br.readLine().length();
      content_disposition = br.readLine();
      content_type = br.readLine();
    } catch (IOException e) {
      e.printStackTrace();
    }

    _start = boundary_length +   content_disposition.length() +
             content_type.length() + 8;

    amount = b.length - _start - boundary_length -6;

    // -6 and -8 have to be added since readLine()
    // returns not the CRLF bytes and for the boundary
    // information we had to cut off the two
    // trailing hyphens and also some CRLF bytes

  }

  // Setup Output Filename
  private final String getFilename() {

    String path_a_file = null;
    String filename = null;

    StringTokenizer st = new StringTokenizer(
                   content_disposition, ";");
    while (st.hasMoreTokens()) {
      path_a_file = st.nextToken();
    }

    st = new StringTokenizer(path_a_file,"\\/");
    while (st.hasMoreTokens()) {
      filename = st.nextToken();
    }
    filename = filename.trim();

    if (filename.substring(0,8).equals("filename")) {
      String s1;
      int i;
      s1 = filename.substring(10);
      i = s1.length() -1;
      _filename = s1.substring(0,i);
      return ( s1.substring(0,i));
    }
    _filename = filename.substring(0,filename.length()-1);
    return (filename.substring(0,filename.length()-1));
  }

  // Write the file to the local Filesystem
  private void writeFile() {

    try {
      BufferedOutputStream  bos =  new BufferedOutputStream(
      new FileOutputStream (WRITE_PATH +
          File.separator + getFilename()));
      bos.write(b,_start,amount);
      bos.flush();
      bos.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  // Show Message to the HTTP Client
  private void response(String filename) {
    HtmlPage hp = new HtmlPage("RESPONSE");
    HtmlBody hb = hp.getBody();
    hb.addItem(new SimpleItem("SUCCESS").setHeading(1));
    hb.addItem("<p>");
    hb.addItem(new SimpleItem("The file " + filename +
                            " has been uploaded").setHeading(3));
    hp.printHeader();
    hp.print();
  }

  public static void main(String[] args) {
    new FileUploadDo();
  }
}

Reading non plain ASCII from HTML forms into Java Servlet Applications

When you want to read extended values (e.g. ISO8859P1: ü,ö,ä) from a HTML Form into a Java Servlet, make sure you set the correct Characterset, before reading the values, or you will get only 7-Bit characters.

// Get HTML-Form Parameters

import java.util.*;
import oracle.html.*;
import oracle.owas.wrb.services.http.HTTP;

public class GetFormParm {

  protected HTTP http = null;

  // Constructor
  public GetFormParm() {
    this.http = HTTP.getRequest();
  }

  // Get URL Parameter for strKey
  public static String getURLParm(HTTP http, String strKey) {
    String strValue;

    // Set URL character set (default ISO-8859-1)
    http.setURLCharacterSet("ISO-8859-1");
    strValue = http.getURLParameter(strKey);
    if (strValue == null) { strValue = ""; }
    if (strValue.compareTo("") == 0) { strValue = ""; }

    return strValue;
  }
}

Java Pitfall: the lost exception

Source: "Thinking in Java, 2nd edition, Revision 10" Chapter 10, http://www.mindview.net/

In general, Java's exception implementation is quite outstanding, but unfortunately there's a flaw. Although exceptions are an indication of a crisis in your program and should never be ignored, it's possible for an exception to simply be lost. This happens with a particular configuration using a finally clause:

// LostMessage.java
// How an exception can be lost.

class VeryImportantException extends Exception {
  public String toString() {
    return "A very important exception!";
  }
}

class HoHumException extends Exception {
  public String toString() {
    return "A trivial exception";
  }
}

public class LostMessage {
  void f() throws VeryImportantException {
    throw new VeryImportantException();
  }
  void dispose() throws HoHumException {
    throw new HoHumException();
  }
  public static void main(String[] args)
      throws Exception {
    LostMessage lm = new LostMessage();
    try {
      lm.f();
    } finally {
      lm.dispose();
    }
  }
}

The output is:

Exception in thread "main" A trivial exception
    at LostMessage.dispose(LostMessage.java:21)
    at LostMessage.main(LostMessage.java:29)

You can see that there's no evidence of the VeryImportantException, which is simply replaced by the HoHumException in the finally clause. This is a rather serious pitfall, since it means that an exception can be completely lost, and in a far more subtle and difficult-to-detect fashion than the example above. In contrast, C++ treats the situation in which a second exception is thrown before the first one is handled as a dire programming error. Perhaps a future version of Java will repair this problem.