1 package example;
2
3 import java.awt;
4 import java.awt.event;
5 import java.io;
6 import java.sql;
7 import org.postgresql.largeobject;
8
9 /**
10 * This example is a small application that stores and displays images
11 * held on a postgresql database.
12 *
13 * Before running this application, you need to create a database, and
14 * on the first time you run it, select "Initialise" in the "PostgreSQL"
15 * menu.
16 *
17 * Important note: You will notice we import the org.postgresql.largeobject
18 * package, but don't import the org.postgresql package. The reason for this is
19 * that importing postgresql can confuse javac (we have conflicting class names
20 * in org.postgresql.* and java.sql.*). This doesn't cause any problems, as
21 * long as no code imports org.postgresql.
22 *
23 * Under normal circumstances, code using any jdbc driver only needs to import
24 * java.sql, so this isn't a problem.
25 *
26 * It's only if you use the non jdbc facilities, do you have to take this into
27 * account.
28 *
29 * Note: For PostgreSQL 6.4, the driver is now Thread safe, so this example
30 * application has been updated to use multiple threads, especially in the
31 * image storing and retrieving methods.
32 */
33
34 public class ImageViewer implements ItemListener
35 {
36 Connection db;
37 Statement stat;
38 LargeObjectManager lom;
39 Frame frame;
40 Label label; // Label used to display the current name
41 List list; // The list of available images
42 imageCanvas canvas; // Canvas used to display the image
43 String currentImage; // The current images name
44
45 // This is a simple component to display our image
46 public class imageCanvas extends Canvas
47 {
48 // holds the image
49 private Image image;
50
51 // holds the background buffer
52 private Image bkg;
53
54 // the size of the buffer
55 private Dimension size;
56
57 public imageCanvas()
58 {
59 image=null;
60 }
61
62 public void setImage(Image img)
63 {
64 image=img;
65 repaint();
66 }
67
68 // This defines our minimum size
69 public Dimension getMinimumSize()
70 {
71 return new Dimension(400,400);
72 }
73
74 public Dimension getPreferedSize()
75 {
76 return getMinimumSize();
77 }
78
79 public void update(Graphics g)
80 {
81 paint(g);
82 }
83
84 /**
85 * Paints the image, using double buffering to prevent screen flicker
86 */
87 public void paint(Graphics gr)
88 {
89 Dimension s = getSize();
90
91 if(size==null || bkg==null || !s.equals(size)) {
92 size = s;
93 bkg = createImage(size.width,size.height);
94 }
95
96 // now set the background
97 Graphics g = bkg.getGraphics();
98 g.setColor(Color.gray);
99 g.fillRect(0,0,s.width,s.height);
100
101 // now paint the image over the background
102 if(image!=null)
103 g.drawImage(image,0,0,this);
104
105 // dispose the graphics instance
106 g.dispose();
107
108 // paint the image onto the component
109 gr.drawImage(bkg,0,0,this);
110
111 }
112
113 }
114
115 public ImageViewer(Frame f,String url,String user,String password) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException
116 {
117 frame = f;
118
119 MenuBar mb = new MenuBar();
120 Menu m;
121 MenuItem i;
122
123 f.setMenuBar(mb);
124 mb.add(m = new Menu("PostgreSQL"));
125 m.add(i= new MenuItem("Initialise"));
126 i.addActionListener(new ActionListener() {
127 public void actionPerformed(ActionEvent e) {
128 ImageViewer.this.init();
129 }
130 });
131
132 m.add(i= new MenuItem("Exit"));
133 ActionListener exitListener = new ActionListener() {
134 public void actionPerformed(ActionEvent e) {
135 ImageViewer.this.close();
136 }
137 };
138 m.addActionListener(exitListener);
139
140 mb.add(m = new Menu("Image"));
141 m.add(i= new MenuItem("Import"));
142 ActionListener importListener = new ActionListener() {
143 public void actionPerformed(ActionEvent e) {
144 ImageViewer.this.importImage();
145 }
146 };
147 i.addActionListener(importListener);
148
149 m.add(i= new MenuItem("Remove"));
150 ActionListener removeListener = new ActionListener() {
151 public void actionPerformed(ActionEvent e) {
152 ImageViewer.this.removeImage();
153 }
154 };
155 i.addActionListener(removeListener);
156
157 // To the north is a label used to display the current images name
158 f.add("North",label = new Label());
159
160 // We have a panel to the south of the frame containing the controls
161 Panel p = new Panel();
162 p.setLayout(new FlowLayout());
163 Button b;
164 p.add(b=new Button("Refresh List"));
165 b.addActionListener(new ActionListener() {
166 public void actionPerformed(ActionEvent e) {
167 ImageViewer.this.refreshList();
168 }
169 });
170 p.add(b=new Button("Import new image"));
171 b.addActionListener(importListener);
172 p.add(b=new Button("Remove image"));
173 b.addActionListener(removeListener);
174 p.add(b=new Button("Quit"));
175 b.addActionListener(exitListener);
176 f.add("South",p);
177
178 // And a panel to the west containing the list of available images
179 f.add("West",list=new List());
180 list.addItemListener(this);
181
182 // Finally the centre contains our image
183 f.add("Center",canvas = new imageCanvas());
184
185 // Load the driver
186 Class.forName("org.postgresql.Driver");
187
188 // Connect to database
189 db = DriverManager.getConnection(url, user, password);
190
191 // Create a statement
192 stat = db.createStatement();
193
194 // Also, get the LargeObjectManager for this connection
195 lom = ((org.postgresql.Connection)db).getLargeObjectAPI();
196
197 // Now refresh the image selection list
198 refreshList();
199 }
200
201
202 /**
203 * This method initialises the database by creating a table that contains
204 * the image names, and Large Object OID's
205 */
206 public void init()
207 {
208 try {
209 //db.setAutoCommit(true);
210 stat.executeUpdate("create table images (imgname name,imgoid oid)");
211 label.setText("Initialised database");
212 db.commit();
213 } catch(SQLException ex) {
214 label.setText(ex.toString());
215 }
216
217 // This must run outside the previous try{} catch{} segment
218 //try {
219 //db.setAutoCommit(true);
220 //} catch(SQLException ex) {
221 //label.setText(ex.toString());
222 //}
223 }
224
225 /**
226 * This closes the connection, and ends the application
227 */
228 public void close()
229 {
230 try {
231 db.close();
232 } catch(SQLException ex) {
233 System.err.println(ex.toString());
234 }
235 System.exit(0);
236 }
237
238 /**
239 * This imports an image into the database, using a Thread to do this in the
240 * background.
241 */
242 public void importImage()
243 {
244 FileDialog d = new FileDialog(frame,"Import Image",FileDialog.LOAD);
245 d.setVisible(true);
246 String name = d.getFile();
247 String dir = d.getDirectory();
248 d.dispose();
249
250 // now start the true importer
251 Thread t = new importer(db,name,dir);
252 //t.setPriority(Thread.MAX_PRIORITY);
253 t.start();
254 }
255
256 /**
257 * This is an example of using a thread to import a file into a Large Object.
258 * It uses the Large Object extension, to write blocks of the file to the
259 * database.
260 */
261 class importer extends Thread
262 {
263 String name,dir;
264 Connection db;
265
266 public importer(Connection db,String name,String dir) {
267 this.db = db;
268 this.name = name;
269 this.dir = dir;
270 }
271
272 public void run() {
273
274 // Now the real import stuff
275 if(name!=null && dir!=null) {
276 Statement stat = null;
277
278 try {
279 // fetch the large object manager
280 LargeObjectManager lom = ((org.postgresql.Connection)db).getLargeObjectAPI();
281
282 db.setAutoCommit(false);
283
284 // A temporary buffer - this can be as large as you like
285 byte buf[] = new byte[2048];
286
287 // Open the file
288 FileInputStream fis = new FileInputStream(new File(dir,name));
289
290 // Now create the large object
291 int oid = lom.create();
292 LargeObject blob = lom.open(oid);
293
294 // Now copy the file into the object.
295 //
296 // Note: we dont use write(buf), as the last block is rarely the same
297 // size as our buffer, so we have to use the amount read.
298 int s,t=0;
299 while((s=fis.read(buf,0,buf.length))>0) {
300 t+=s;
301 blob.write(buf,0,s);
302 }
303
304 // Close the object
305 blob.close();
306
307 // Now store the entry into the table
308
309 // As we are a different thread to the other window, we must use
310 // our own thread
311 stat = db.createStatement();
312 stat.executeUpdate("insert into images values ('"+name+"',"+oid+")");
313 db.commit();
314 db.setAutoCommit(false);
315
316 // Finally refresh the names list, and display the current image
317 ImageViewer.this.refreshList();
318 ImageViewer.this.displayImage(name);
319 } catch(Exception ex) {
320 label.setText(ex.toString());
321 } finally {
322 // ensure the statement is closed after us
323 try {
324 if(stat != null)
325 stat.close();
326 } catch(SQLException se) {
327 System.err.println("closing of Statement failed");
328 }
329 }
330 }
331 }
332 }
333
334 /**
335 * This refreshes the list of available images
336 */
337 public void refreshList()
338 {
339 try {
340 // First, we'll run a query, retrieving all of the image names
341 ResultSet rs = stat.executeQuery("select imgname from images order by imgname");
342 if(rs!=null) {
343 list.removeAll();
344 while(rs.next())
345 list.addItem(rs.getString(1));
346 rs.close();
347 }
348 } catch(SQLException ex) {
349 label.setText(ex.toString()+" Have you initialised the database?");
350 }
351 }
352
353 /**
354 * This removes an image from the database
355 *
356 * Note: With postgresql, this is the only way of deleting a large object
357 * using Java.
358 */
359 public void removeImage()
360 {
361 try {
362 //
363 // Delete any large objects for the current name
364 //
365 // Note: We don't need to worry about being in a transaction
366 // here, because we are not opening any blobs, only deleting
367 // them
368 //
369 ResultSet rs = stat.executeQuery("select imgoid from images where imgname='"+currentImage+"'");
370 if(rs!=null) {
371 // Even though there should only be one image, we still have to
372 // cycle through the ResultSet
373 while(rs.next()) {
374 lom.delete(rs.getInt(1));
375 }
376 }
377 rs.close();
378
379 // Finally delete any entries for that name
380 stat.executeUpdate("delete from images where imgname='"+currentImage+"'");
381
382 label.setText(currentImage+" deleted");
383 currentImage=null;
384 refreshList();
385 } catch(SQLException ex) {
386 label.setText(ex.toString());
387 }
388 }
389
390 /**
391 * This displays an image from the database.
392 *
393 * For images, this is the easiest method.
394 */
395 public void displayImage(String name)
396 {
397 try {
398 //
399 // Now as we are opening and reading a large object we must
400 // turn on Transactions. This includes the ResultSet.getBytes()
401 // method when it's used on a field of type oid!
402 //
403 db.setAutoCommit(false);
404
405 ResultSet rs = stat.executeQuery("select imgoid from images where imgname='"+name+"'");
406 if(rs!=null) {
407 // Even though there should only be one image, we still have to
408 // cycle through the ResultSet
409 while(rs.next()) {
410 canvas.setImage(canvas.getToolkit().createImage(rs.getBytes(1)));
411 label.setText(currentImage = name);
412 }
413 }
414 rs.close();
415 } catch(SQLException ex) {
416 label.setText(ex.toString());
417 } finally {
418 try {
419 db.setAutoCommit(true);
420 } catch(SQLException ex2) {
421 }
422 }
423 }
424
425 public void itemStateChanged(ItemEvent e) {
426 displayImage(list.getItem(((Integer)e.getItem()).intValue()));
427 }
428
429 /**
430 * This is the command line instructions
431 */
432 public static void instructions()
433 {
434 System.err.println("java example.ImageViewer jdbc-url user password");
435 System.err.println("\nExamples:\n");
436 System.err.println("java -Djdbc.driver=org.postgresql.Driver example.ImageViewer jdbc:postgresql:test postgres password\n");
437
438 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.");
439 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.");
440 }
441
442 /**
443 * This is the application entry point
444 */
445 public static void main(String args[])
446 {
447 if(args.length!=3) {
448 instructions();
449 System.exit(1);
450 }
451
452 try {
453 Frame frame = new Frame("PostgreSQL ImageViewer v7.0 rev 1");
454 frame.setLayout(new BorderLayout());
455 ImageViewer viewer = new ImageViewer(frame,args[0],args[1],args[2]);
456 frame.pack();
457 frame.setLocation(0,50);
458 frame.setVisible(true);
459 } catch(Exception ex) {
460 System.err.println("Exception caught.\n"+ex);
461 ex.printStackTrace();
462 }
463 }
464 }