package example ;
import java.awt.* ;
import java.awt.event.* ;
import java.io.* ;
import java.sql.* ;
import org.postgresql.largeobject.* ;
/ * *
* This example is a small application that stores and displays images
* held on a postgresql database .
*
* Before running this application , you need to create a database , and
* on the first time you run it , select "Initialise" in the "PostgreSQL"
* menu .
*
* Important note : You will notice we import the org . postgresql . largeobject
* package , but don ' t import the org . postgresql package . The reason for this is
* that importing postgresql can confuse javac ( we have conflicting class names
* in org . postgresql . * and java . sql . * ) . This doesn ' t cause any problems , as
* long as no code imports org . postgresql .
*
* Under normal circumstances , code using any jdbc driver only needs to import
* java . sql , so this isn ' t a problem .
*
* It ' s only if you use the non jdbc facilities , do you have to take this into
* account .
*
* Note : For PostgreSQL 6 . 4 , the driver is now Thread safe , so this example
* application has been updated to use multiple threads , especially in the
* image storing and retrieving methods .
* /
public class ImageViewer implements ItemListener
{
Connection db ;
Statement stat ;
LargeObjectManager lom ;
Frame frame ;
Label label ; // Label used to display the current name
List list ; // The list of available images
imageCanvas canvas ; // Canvas used to display the image
String currentImage ; // The current images name
// This is a simple component to display our image
public class imageCanvas extends Canvas
{
// holds the image
private Image image ;
// holds the background buffer
private Image bkg ;
// the size of the buffer
private Dimension size ;
public imageCanvas ( )
{
image = null ;
}
public void setImage ( Image img )
{
image = img ;
repaint ( ) ;
}
// This defines our minimum size
public Dimension getMinimumSize ( )
{
return new Dimension ( 400 , 400 ) ;
}
public Dimension getPreferedSize ( )
{
return getMinimumSize ( ) ;
}
public void update ( Graphics g )
{
paint ( g ) ;
}
/ * *
* Paints the image , using double buffering to prevent screen flicker
* /
public void paint ( Graphics gr )
{
Dimension s = getSize ( ) ;
if ( size = = null | | bkg = = null | | ! s . equals ( size ) ) {
size = s ;
bkg = createImage ( size . width , size . height ) ;
}
// now set the background
Graphics g = bkg . getGraphics ( ) ;
g . setColor ( Color . gray ) ;
g . fillRect ( 0 , 0 , s . width , s . height ) ;
// now paint the image over the background
if ( image ! = null )
g . drawImage ( image , 0 , 0 , this ) ;
// dispose the graphics instance
g . dispose ( ) ;
// paint the image onto the component
gr . drawImage ( bkg , 0 , 0 , this ) ;
}
}
public ImageViewer ( Frame f , String url , String user , String password ) throws ClassNotFoundException , FileNotFoundException , IOException , SQLException
{
frame = f ;
MenuBar mb = new MenuBar ( ) ;
Menu m ;
MenuItem i ;
f . setMenuBar ( mb ) ;
mb . add ( m = new Menu ( "PostgreSQL" ) ) ;
m . add ( i = new MenuItem ( "Initialise" ) ) ;
i . addActionListener ( new ActionListener ( ) {
public void actionPerformed ( ActionEvent e ) {
ImageViewer . this . init ( ) ;
}
} ) ;
m . add ( i = new MenuItem ( "Exit" ) ) ;
ActionListener exitListener = new ActionListener ( ) {
public void actionPerformed ( ActionEvent e ) {
ImageViewer . this . close ( ) ;
}
} ;
m . addActionListener ( exitListener ) ;
mb . add ( m = new Menu ( "Image" ) ) ;
m . add ( i = new MenuItem ( "Import" ) ) ;
ActionListener importListener = new ActionListener ( ) {
public void actionPerformed ( ActionEvent e ) {
ImageViewer . this . importImage ( ) ;
}
} ;
i . addActionListener ( importListener ) ;
m . add ( i = new MenuItem ( "Remove" ) ) ;
ActionListener removeListener = new ActionListener ( ) {
public void actionPerformed ( ActionEvent e ) {
ImageViewer . this . removeImage ( ) ;
}
} ;
i . addActionListener ( removeListener ) ;
// To the north is a label used to display the current images name
f . add ( "North" , label = new Label ( ) ) ;
// We have a panel to the south of the frame containing the controls
Panel p = new Panel ( ) ;
p . setLayout ( new FlowLayout ( ) ) ;
Button b ;
p . add ( b = new Button ( "Refresh List" ) ) ;
b . addActionListener ( new ActionListener ( ) {
public void actionPerformed ( ActionEvent e ) {
ImageViewer . this . refreshList ( ) ;
}
} ) ;
p . add ( b = new Button ( "Import new image" ) ) ;
b . addActionListener ( importListener ) ;
p . add ( b = new Button ( "Remove image" ) ) ;
b . addActionListener ( removeListener ) ;
p . add ( b = new Button ( "Quit" ) ) ;
b . addActionListener ( exitListener ) ;
f . add ( "South" , p ) ;
// And a panel to the west containing the list of available images
f . add ( "West" , list = new List ( ) ) ;
list . addItemListener ( this ) ;
// Finally the centre contains our image
f . add ( "Center" , canvas = new imageCanvas ( ) ) ;
// Load the driver
Class . forName ( "org.postgresql.Driver" ) ;
// Connect to database
db = DriverManager . getConnection ( url , user , password ) ;
// Create a statement
stat = db . createStatement ( ) ;
// Also, get the LargeObjectManager for this connection
lom = ( ( org . postgresql . Connection ) db ) . getLargeObjectAPI ( ) ;
// Now refresh the image selection list
refreshList ( ) ;
}
/ * *
* This method initialises the database by creating a table that contains
* the image names , and Large Object OID ' s
* /
public void init ( )
{
try {
//db.setAutoCommit(true);
stat . executeUpdate ( "create table images (imgname name,imgoid oid)" ) ;
label . setText ( "Initialised database" ) ;
db . commit ( ) ;
} catch ( SQLException ex ) {
label . setText ( ex . toString ( ) ) ;
}
// This must run outside the previous try{} catch{} segment
//try {
//db.setAutoCommit(true);
//} catch(SQLException ex) {
//label.setText(ex.toString());
//}
}
/ * *
* This closes the connection , and ends the application
* /
public void close ( )
{
try {
db . close ( ) ;
} catch ( SQLException ex ) {
System . err . println ( ex . toString ( ) ) ;
}
System . exit ( 0 ) ;
}
/ * *
* This imports an image into the database , using a Thread to do this in the
* background .
* /
public void importImage ( )
{
FileDialog d = new FileDialog ( frame , "Import Image" , FileDialog . LOAD ) ;
d . setVisible ( true ) ;
String name = d . getFile ( ) ;
String dir = d . getDirectory ( ) ;
d . dispose ( ) ;
// now start the true importer
Thread t = new importer ( db , name , dir ) ;
//t.setPriority(Thread.MAX_PRIORITY);
t . start ( ) ;
}
/ * *
* This is an example of using a thread to import a file into a Large Object .
* It uses the Large Object extension , to write blocks of the file to the
* database .
* /
class importer extends Thread
{
String name , dir ;
Connection db ;
public importer ( Connection db , String name , String dir ) {
this . db = db ;
this . name = name ;
this . dir = dir ;
}
public void run ( ) {
// Now the real import stuff
if ( name ! = null & & dir ! = null ) {
Statement stat = null ;
try {
// fetch the large object manager
LargeObjectManager lom = ( ( org . postgresql . Connection ) db ) . getLargeObjectAPI ( ) ;
db . setAutoCommit ( false ) ;
// A temporary buffer - this can be as large as you like
byte buf [ ] = new byte [ 2048 ] ;
// Open the file
FileInputStream fis = new FileInputStream ( new File ( dir , name ) ) ;
// Now create the large object
int oid = lom . create ( ) ;
LargeObject blob = lom . open ( oid ) ;
// Now copy the file into the object.
//
// Note: we dont use write(buf), as the last block is rarely the same
// size as our buffer, so we have to use the amount read.
int s , t = 0 ;
while ( ( s = fis . read ( buf , 0 , buf . length ) ) > 0 ) {
t + = s ;
blob . write ( buf , 0 , s ) ;
}
// Close the object
blob . close ( ) ;
// Now store the entry into the table
// As we are a different thread to the other window, we must use
// our own thread
stat = db . createStatement ( ) ;
stat . executeUpdate ( "insert into images values ('" + name + "'," + oid + ")" ) ;
db . commit ( ) ;
db . setAutoCommit ( false ) ;
// Finally refresh the names list, and display the current image
ImageViewer . this . refreshList ( ) ;
ImageViewer . this . displayImage ( name ) ;
} catch ( Exception ex ) {
label . setText ( ex . toString ( ) ) ;
} finally {
// ensure the statement is closed after us
try {
if ( stat ! = null )
stat . close ( ) ;
} catch ( SQLException se ) {
System . err . println ( "closing of Statement failed" ) ;
}
}
}
}
}
/ * *
* This refreshes the list of available images
* /
public void refreshList ( )
{
try {
// First, we'll run a query, retrieving all of the image names
ResultSet rs = stat . executeQuery ( "select imgname from images order by imgname" ) ;
if ( rs ! = null ) {
list . removeAll ( ) ;
while ( rs . next ( ) )
list . addItem ( rs . getString ( 1 ) ) ;
rs . close ( ) ;
}
} catch ( SQLException ex ) {
label . setText ( ex . toString ( ) + " Have you initialised the database?" ) ;
}
}
/ * *
* This removes an image from the database
*
* Note : With postgresql , this is the only way of deleting a large object
* using Java .
* /
public void removeImage ( )
{
try {
//
// Delete any large objects for the current name
//
// Note: We don't need to worry about being in a transaction
// here, because we are not opening any blobs, only deleting
// them
//
ResultSet rs = stat . executeQuery ( "select imgoid from images where imgname='" + currentImage + "'" ) ;
if ( rs ! = null ) {
// Even though there should only be one image, we still have to
// cycle through the ResultSet
while ( rs . next ( ) ) {
lom . delete ( rs . getInt ( 1 ) ) ;
}
}
rs . close ( ) ;
// Finally delete any entries for that name
stat . executeUpdate ( "delete from images where imgname='" + currentImage + "'" ) ;
label . setText ( currentImage + " deleted" ) ;
currentImage = null ;
refreshList ( ) ;
} catch ( SQLException ex ) {
label . setText ( ex . toString ( ) ) ;
}
}
/ * *
* This displays an image from the database .
*
* For images , this is the easiest method .
* /
public void displayImage ( String name )
{
try {
//
// Now as we are opening and reading a large object we must
// turn on Transactions. This includes the ResultSet.getBytes()
// method when it's used on a field of type oid!
//
db . setAutoCommit ( false ) ;
ResultSet rs = stat . executeQuery ( "select imgoid from images where imgname='" + name + "'" ) ;
if ( rs ! = null ) {
// Even though there should only be one image, we still have to
// cycle through the ResultSet
while ( rs . next ( ) ) {
canvas . setImage ( canvas . getToolkit ( ) . createImage ( rs . getBytes ( 1 ) ) ) ;
label . setText ( currentImage = name ) ;
}
}
rs . close ( ) ;
} catch ( SQLException ex ) {
label . setText ( ex . toString ( ) ) ;
} finally {
try {
db . setAutoCommit ( true ) ;
} catch ( SQLException ex2 ) {
}
}
}
public void itemStateChanged ( ItemEvent e ) {
displayImage ( list . getItem ( ( ( Integer ) e . getItem ( ) ) . intValue ( ) ) ) ;
}
/ * *
* This is the command line instructions
* /
public static void instructions ( )
{
System . err . println ( "java example.ImageViewer jdbc-url user password" ) ;
System . err . println ( "\nExamples:\n" ) ;
System . err . println ( "java -Djdbc.driver=org.postgresql.Driver example.ImageViewer jdbc:postgresql:test postgres password\n" ) ;
System . err . println ( "This example tests the binary large object api of the driver.\nBasically, it will allow you to store and view images held in the database." ) ;
System . err . println ( "Note: If you are running this for the first time on a particular database,\nyou have to select \"Initialise\" in the \"PostgreSQL\" menu.\nThis will create a table used to store image names." ) ;
}
/ * *
* This is the application entry point
* /
public static void main ( String args [ ] )
{
if ( args . length ! = 3 ) {
instructions ( ) ;
System . exit ( 1 ) ;
}
try {
Frame frame = new Frame ( "PostgreSQL ImageViewer v7.0 rev 1" ) ;
frame . setLayout ( new BorderLayout ( ) ) ;
ImageViewer viewer = new ImageViewer ( frame , args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
frame . pack ( ) ;
frame . setLocation ( 0 , 50 ) ;
frame . setVisible ( true ) ;
} catch ( Exception ex ) {
System . err . println ( "Exception caught.\n" + ex ) ;
ex . printStackTrace ( ) ;
}
}
}