source: other-projects/trunk/gs3-webservices-democlient/src/GS3DemoClient/org/greenstone/gs3client/GS3JavaClient.java@ 15440

Last change on this file since 15440 was 15440, checked in by ak19, 16 years ago

Fixed display of exception output displayed in JOptionPane. Previously the last word could be swallowed and not displayed.

File size: 41.8 KB
Line 
1/**
2 *#########################################################################
3 * GS3JavaClient.java - part of the demo-client for Greenstone 3, of the
4 * Greenstone digital library suite from the New Zealand Digital Library
5 * Project at the * University of Waikato, New Zealand.
6 * <BR><BR>
7 * Copyright (C) 2008 New Zealand Digital Library Project
8 * <BR><BR>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 * <BR><BR>
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *########################################################################
19 */
20
21package org.greenstone.gs3client;
22
23
24import org.apache.log4j.Logger;
25import org.apache.log4j.PropertyConfigurator;
26import org.greenstone.gs3client.BrowseDisplay.ClassifierData;
27
28import org.greenstone.gs3client.data.BrowseResponseData;
29import org.greenstone.gs3client.data.CollectionData;
30import org.greenstone.gs3client.data.DocumentNodeData;
31import org.greenstone.gs3client.data.ClassifierNodeData;
32import org.greenstone.gs3client.data.NodeData;
33import org.greenstone.gs3client.data.ParseUtil;
34import org.greenstone.gs3client.data.QueryResponseData;
35import org.greenstone.gs3client.data.ResponseData;
36//By importing this *static* inner class of CollectionData, can refer
37//to it by its unqualified name 'ServiceData'. (Learnt this from
38//http://today.java.net/pub/a/today/2006/03/23/multi-split-pane.html)
39import org.greenstone.gs3client.data.CollectionData.ServiceData;
40import org.greenstone.gs3client.dlservices.DigitalLibraryServicesAPIA;
41import org.greenstone.gs3client.dlservices.FedoraServicesAPIA;
42import org.greenstone.gs3client.dlservices.GS3ServicesAPIA;
43
44import javax.swing.*;
45import java.awt.BorderLayout;
46import java.awt.GridLayout;
47import java.awt.FlowLayout;
48import java.awt.event.ActionListener;
49import java.awt.event.ActionEvent;
50import java.awt.Container;
51import java.awt.Toolkit; // for making this JFrame client fullsize
52
53import java.net.Authenticator;
54import java.net.PasswordAuthentication;
55
56import org.w3c.dom.Document;
57import org.w3c.dom.Element;
58import org.w3c.dom.NodeList;
59
60import java.util.Vector;
61import java.util.HashMap;
62
63import java.io.StringReader;
64import javax.xml.parsers.DocumentBuilderFactory;
65import javax.xml.parsers.DocumentBuilder;
66import org.xml.sax.InputSource;
67
68import org.greenstone.gsdl3.util.GSXML;
69import java.awt.Cursor;
70
71/*
72 To make this class compile with Java 5/Java 1.5 need to rename the file
73 xalan.jar.tmp (located in $GS3_HOME/web/WEB-INF/lib) to xalan.jar
74 Without doing that, it will complain (unless compiled with Java 1.4.2).
75
76 RUNTIME Configuration of this class:
77 - this client needs the jar files:
78 log4j, xercesImpl, mail, lucene, javagdbm,
79 And possibly: xml-apis, activation, axis, mg, mgpp
80 - VM arguments: -Djava.library.path=/research/ak19/greenstone3/lib/jni
81
82 COMPILE Configuration of this class:
83 - BUILD PATH needs the jar files:
84 gsdl3 (or just the class GSXML), jaxrpc, axis
85 */
86/**
87 * The main Javaclient class. Creates the main window containing:
88 * - the radio button group for making either Greenstone 3 or Fedora the active DL.
89 * - the comboboxes for selecting the collection and the service therein
90 * - three panes - for executing queries, viewing search results and browsing.
91 * @author ak19
92*/
93public class GS3JavaClient extends JFrame implements ActionListener {
94 // static final long serialVersionUID = 1; //eclipse keeps warning
95 /** The Logger for this class */
96 static Logger LOG = Logger.getLogger(GS3JavaClient.class);
97
98 /** The value of the SEARCHING activity */
99 public static int SEARCHING = 0;
100 /** The value of the BROWSING activity */
101 public static int BROWSING = 1;
102 /** we want to display a drop-down box for the services available,
103 * but only Services that can be executed (searching and browsing) */
104 protected static final boolean executableServicesOnly = true;
105
106 /** To keep track of whether we are searching or browsing.
107 * Value of activity can be SEARCHING OR BROWSING */
108 protected int activity = 0; //
109
110 /** Reference to a digital library instance (Greenstone 3 or Fedora) that
111 * allows this client to execute services offered by the digital library */
112 protected DigitalLibraryServicesAPIA dlAPIA = null;
113 /** Reference to the object that interacts with Greenstone3's web services */
114 protected GS3ServicesAPIA greenstoneDL = null;
115 /** Reference to the object that interacts with the FedoraGS3.jar component */
116 protected FedoraServicesAPIA fedoraDL = null;
117
118 /** Object that stores the data of a query response XML message */
119 protected QueryResponseData queryResponseObj = null;
120
121 /** Object that stores the data of a browse response XML message
122 * (response for classification hierarchies) */
123 protected BrowseResponseData browseResponseObject = null;
124
125 /** Preferred language of display items in responses.
126 * "" or "en" for English */
127 protected String lang = "";
128
129 /** The currently selected collection */
130 protected String colName = "";
131 /** The currently selected service */
132 protected String serviceName = "";
133
134 /* The always-visible GUI objects of this main window */
135 protected JComboBox dlChooser = null;
136 protected JTextField proxyhostField = null;
137 protected JTextField proxyportField = null;
138 protected JTextField nonProxyHostNamesField = null;
139 protected JButton setProxySettings = null;
140
141 protected JComboBox serviceBox = null;
142 protected JComboBox collBox = null;
143 protected JButton collInfoButton = null;
144 protected JButton searchButton = null;
145 protected JTabbedPane tabbedPane = null;
146
147 protected static final int SELECT = 0;
148 protected static final int GREENSTONE = 1;
149 protected static final int FEDORA = 2;
150 protected static final String[] dlOptions = {"select", "greenstone", "fedora"};
151
152 /* Components of the query tab */
153 protected QueryForm queryPanel = null;
154 protected JTextField collectionNameField = null;
155
156 /* Components of the search tab */
157 protected SearchResultsDisplay searchResultsDisplay = null;
158 protected JPanel searchPanel = null;
159 protected JTextArea searchSummary = null;
160
161 /* Components of the browse tab */
162 protected BrowseDisplay browsePanel = null;
163
164 /** @return the language of the display items */
165 public String getLanguage() { return lang; }
166 /** Set the preferred language of the display items
167 * @param lang - the preferred language for display values */
168 public void setLanguage(String lang) { this.lang = lang; }
169
170 /** Inner class that handles authentication for any sites (urls, etc.)
171 * that require it. A dialog pops up to request username and password
172 * details from the user for each site's realm that needs authentication.
173 */
174 static class PasswordAuthenticator extends Authenticator {
175 protected PasswordAuthentication getPasswordAuthentication() {
176 JTextField username = new JTextField();
177 JTextField password = new JPasswordField();
178 JPanel panel = new JPanel(new GridLayout(2,2));
179 panel.add(new JLabel("User Name"));
180 panel.add(username);
181 panel.add(new JLabel("Password") );
182 panel.add(password);
183 int option = JOptionPane.showConfirmDialog(null, new Object[] {
184 "Site: "+getRequestingHost(),
185 "Realm: "+getRequestingPrompt(), panel},
186 "Enter Network Password",
187 JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
188 if ( option == JOptionPane.OK_OPTION ) {
189 return new PasswordAuthentication(
190 username.getText(), password.getText().toCharArray());
191 } else {
192 return null;
193 }
194 }
195 }
196
197 /** Default constructor that creates and initialises this main
198 * window and its internal GUI components */
199 public GS3JavaClient() {
200 super();
201 this.setTitle("Greenstone 3 web services demo-client");
202
203 // digital library panel lets the user choose the digital library
204 // repository to access and to set the proxy settings
205 JPanel dlPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
206 dlChooser = new JComboBox(dlOptions);
207 dlPanel.add(new JLabel("Use digital library:"));
208 dlPanel.add(dlChooser);
209 dlChooser.addActionListener(this);
210 dlPanel.setBorder(BorderFactory.createTitledBorder(
211 "Digital library settings"));
212
213 this.proxyhostField = new JTextField(15);
214 this.proxyportField = new JTextField(4);
215 this.nonProxyHostNamesField = new JTextField(20);
216 this.setProxySettings = new JButton("Set");
217 nonProxyHostNamesField.setToolTipText("The hosts for which no proxies are required "
218 + "(separate hosts with |, * wildcard allowed)");
219 JPanel topProxyPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
220 topProxyPanel.add(new JLabel("Proxy host:"));
221 topProxyPanel.add(proxyhostField);
222 topProxyPanel.add(new JLabel("Proxy port:"));
223 topProxyPanel.add(proxyportField);
224 JPanel bottomProxyPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
225 bottomProxyPanel.add(new JLabel("No proxy for:"));
226 bottomProxyPanel.add(nonProxyHostNamesField);
227 bottomProxyPanel.add(setProxySettings);
228 setProxySettings.addActionListener(this);
229 JPanel proxyPanel = new JPanel(new BorderLayout());
230 proxyPanel.add(topProxyPanel, BorderLayout.CENTER);
231 proxyPanel.add(bottomProxyPanel, BorderLayout.SOUTH);
232 proxyPanel.setBorder(BorderFactory.createTitledBorder(
233 "Connection settings"));
234
235 // Add the dlPanel and proxyPanel next to each other at the top
236 JPanel topPanel = new JPanel(new BorderLayout());
237 topPanel.add(dlPanel, BorderLayout.CENTER);
238 topPanel.add(proxyPanel, BorderLayout.EAST);
239
240 // Setting up the GUI of this client
241 // create a JComboBox, and an item in it for each collection
242 collBox = new JComboBox();
243 serviceBox = new JComboBox();
244
245 JPanel collPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
246 collPanel.add(new JLabel("Choose collection:"));
247 collPanel.add(collBox);
248 // displayName
249 collPanel.add(new JLabel("Full name:"));
250 collectionNameField = new JTextField(18);
251 collectionNameField.setEditable(false);
252 collectionNameField.setCaretPosition(0);
253 collPanel.add(collectionNameField);
254 // detailed information button, shows collection's description
255 collInfoButton = new JButton("Info");
256 collInfoButton.addActionListener(this);
257 collPanel.add(collInfoButton);
258
259 JPanel servicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
260 servicePanel.add(new JLabel("Available services:"));
261 servicePanel.add(serviceBox);
262
263 JPanel collServicesPanel = new JPanel(new BorderLayout());
264 collServicesPanel.add(collPanel, BorderLayout.NORTH);
265 collServicesPanel.add(servicePanel, BorderLayout.SOUTH);
266
267 tabbedPane = new JTabbedPane(); // tabs on top
268 queryPanel = new QueryForm(this);
269 tabbedPane.add("Query", queryPanel);
270 searchResultsDisplay = new SearchResultsDisplay(this);
271 searchResultsDisplay.setOpaque(true); //necessary for JTree
272
273 JScrollPane searchView = new JScrollPane(searchResultsDisplay);
274 searchSummary = new JTextArea();
275 searchSummary.setEditable(false);
276
277 searchPanel = new JPanel(new BorderLayout());
278 searchPanel.add(searchView, BorderLayout.CENTER);
279 searchPanel.add(searchSummary, BorderLayout.NORTH);
280 tabbedPane.add("Search Results", searchPanel);
281
282 JPanel activityPanel = new JPanel(new BorderLayout());
283 activityPanel.add(collServicesPanel, BorderLayout.NORTH);
284 activityPanel.add(tabbedPane, BorderLayout.CENTER);
285
286 Container c = this.getContentPane();
287 c.setLayout(new BorderLayout()); // new BoxLayout(c, BoxLayout.Y_AXIS));
288 c.add(topPanel, BorderLayout.NORTH);
289 c.add(activityPanel, BorderLayout.CENTER);
290
291 // browse panel
292 browsePanel = new BrowseDisplay(this);
293 tabbedPane.add("Browse", browsePanel);
294
295 // add actionlisteners
296 collBox.addActionListener(this);
297 serviceBox.addActionListener(this);
298
299 // GUI DONE, now instantiate the DATA and DIGITAL LIBRARY objects
300 // instantiate the objects that deal with storing query response data
301 // and browse response data
302 queryResponseObj = new QueryResponseData();
303 browseResponseObject = new BrowseResponseData();
304
305 // The java.net.Authenticator can be used to send user credentials
306 // when needed. When no username/password are provided then a popup
307 // is shown to ask for the credentials. *Only* when a site/realm
308 // requires password and username does the Authenticator show the
309 // dialog. For example, the dialog will appear when pwd & username
310 // are needed for proxy authorisation.
311 Authenticator.setDefault(new PasswordAuthenticator());
312
313 changeDL(null); // no dl selected
314
315 // Set the selection colours that will be uniform for all DLs
316 UIManager.put("Tree.textBackground", ColourCombo.transparent);
317 // Instead of the white tree text background, set it to transparent
318 UIManager.put("Tree.selectionBackground", ColourCombo.yellow);
319 UIManager.put("TabbedPane.selected", ColourCombo.yellow);
320 UIManager.put("TabbedPane.selected", ColourCombo.yellow);
321 UIManager.put("ComboBox.selectionBackground", ColourCombo.yellow);
322 UIManager.put("List.selectionBackground", ColourCombo.yellow);
323 UIManager.put("TextArea.selectionBackground", ColourCombo.yellow);
324 UIManager.put("TextField.selectionBackground", ColourCombo.yellow);
325 // force the UI to load these new UIManager defaults
326 javax.swing.SwingUtilities.updateComponentTreeUI(this);
327 // We can't change the UIManager defaults (properly) throughout GUI
328 // display-time, only upon creation and first display. This is
329 // because updateComponentTreeUI() has no effect once components
330 // have been displayed. For this reason, the various changeUIColor()
331 // methods and class ColourCombo have been written.
332 }
333
334 /** Attempts to establish a connection to GS3 Web services and instantiate
335 * a GS3ServicesAPIA object and store it in the member variable greenstoneDL.
336 * If it was unsuccessful, then this variable remains at null and an error
337 * message is displayed. */
338 protected boolean createGreenstoneDLConnection()
339 {
340 // store the old choice of dl, if there was any
341 int oldSetting = (dlAPIA == null) ? SELECT : FEDORA;
342 // try instantiating a GS3 dl handle
343 try {
344 greenstoneDL = new GS3ServicesAPIA();
345 return true; // success
346 } catch(DigitalLibraryServicesAPIA.CancelException e){
347 // The user pressed cancel in the gs3 services instantiation dialog
348 // Set the dl drop-down list option back to whatever it was
349 } catch(Exception e) {
350 // still unable to instantiate greenstone, but show
351 // the error for what went wrong
352 JOptionPane.showMessageDialog(
353 null,
354 "Error connecting to Greenstone 3 QBRSOAPServer web services.\n"
355 + "When attempting to process its wsdl file, encountered the problem:\n"
356 + e,
357 "Fatal error instantiating Digital Library Services interface. Exiting...",
358 JOptionPane.ERROR_MESSAGE
359 );
360 //e.printStackTrace(System.err);
361 LOG.error("Error connecting to Greenstone 3 web services:\n" + e);
362 }
363 // We'd be here if an exception occurred. Need to back to the old dl
364 // settings
365 dlChooser.setSelectedIndex(oldSetting);
366 return false;
367 }
368
369 /** Attempts to establish a connection to FedoraGS3.jar and instantiate
370 * a FedoraServicesAPIA object and store it in the member variable fedoraDL.
371 * If it was unsuccessful, then this variable remains at null and an error
372 * message is displayed. */
373 protected boolean createFedoraDLConnection()
374 {
375 // store the old choice of dl, if there was any
376 int oldSetting = oldSetting = (dlAPIA == null) ? SELECT : GREENSTONE;
377 // Try to instantiate a Fedora dl handle
378 try {
379 fedoraDL = new FedoraServicesAPIA();
380 return true; // success
381 } catch(
382 org.greenstone.fedora.services.FedoraGS3Exception.CancelledException e)
383 {
384 // The user pressed cancel in the fedora services instantiation dlg
385 // Set the dl drop-down list option back to whatever it was
386 } catch(Exception e) {
387 JOptionPane.showMessageDialog(
388 null,
389 "Error instantiating the interface to the Fedora Repository\n"+ e,
390 "Unable to connect to Fedora's digital library services.",
391 JOptionPane.ERROR_MESSAGE
392 );
393 //e.printStackTrace(System.err);
394 LOG.error(
395 "Error instantiating the interface to the Fedora Repository:\n"+ e);
396 }
397 // We'd be here if an exception occurred. Need to go back to the old dl
398 // settings
399 dlChooser.setSelectedIndex(oldSetting);
400 return false;
401 }
402
403 /** Method that is called when the user chooses to change the active
404 * digital library (between Greenstone3 and Fedora).
405 * @param dl is the new active digital library (the
406 * DigitalLibraryServicesAPIA object to change to). */
407 protected void changeDL(DigitalLibraryServicesAPIA dl) {
408 // change the colour of the interface
409 this.changeUIColour(dlChooser.getSelectedIndex());
410
411 // clear up remnants of previously selected digital library's
412 // processing
413 browsePanel.clear();
414 searchResultsDisplay.clear();
415 this.searchSummary.setText("");
416 browseResponseObject.clear();
417 queryResponseObj.clear();
418
419 // set current digital library to selected one
420 dlAPIA = dl;
421
422 if(dlAPIA == null) {
423 // clear some panels so the user can't do anything
424 this.queryPanel.clear();
425 collBox.removeAllItems();
426 collBox.setEnabled(false);
427 serviceBox.removeAllItems();
428 serviceBox.setEnabled(false);
429 this.collectionNameField.setText("");
430 this.collInfoButton.setEnabled(false);
431 return;
432 }
433
434 // start retrieving the information related to the chosen dl
435 String response = dlAPIA.describe();
436 if(response == null) {
437 LOG.error("Error: no response from dlAPIA.describe() of "
438 + dlAPIA.getDisplayName());
439 System.exit(1);
440 }
441
442 Element responseXML = getResponseAsDOM(
443 "DESCRIBE RESPONSE:\n", response);
444
445 // Get the collectionList element of the response
446 // from the default messageRouter-describe:
447 Element collectionList = ParseUtil.getFirstDescElementCalled(
448 responseXML, // (<message>, "collectionList")
449 GSXML.COLLECTION_ELEM+GSXML.LIST_MODIFIER
450 );
451 if(collectionList == null)
452 LOG.error("collectionList is null!");
453
454 // Next, create a CollectionData object for each collection using
455 // its name
456 CollectionData[] collData = null;
457 Vector v = ParseUtil.getAllChildElementsCalled(collectionList,
458 GSXML.COLLECTION_ELEM); // (<collectionList>, "collection")
459 if(v == null)
460 LOG.error("collections are null!");
461
462 if(v != null) {
463 StringBuffer buf = new StringBuffer("Collections:\n");
464 collData = new CollectionData[v.size()];
465 for(int i = 0; i < v.size(); i++){
466 collData[i] = new CollectionData((Element)v.get(i));
467 buf.append(collData[i].name + ", ");
468 }
469 }
470
471 // Now send a describe request to each collection
472 // And set the fields for each collData using the response
473 for(int i = 0; i < collData.length; i++){
474 response = dlAPIA.describeCollection(collData[i].name);
475 responseXML = getResponseAsDOM(
476 "describeCollection "+collData[i].name+":", response);
477 Element collectionTag = ParseUtil.getFirstDescElementCalled(
478 responseXML, GSXML.COLLECTION_ELEM);
479
480 // set the other fields of the CollectionData object using
481 // the collectionTag of the response from Collection-describe:
482 collData[i].setFields(collectionTag);
483 }
484
485 if(collData == null) {
486 LOG.debug("No collections in the chosen repository");
487 return;
488 } else {
489 this.collInfoButton.setEnabled(true);
490 collBox.setEnabled(true);
491 serviceBox.setEnabled(true);
492 }
493
494 // fill in the combobox containing the collections and the one
495 // containing the services
496 collBox.removeAllItems();
497 for(int i = 0; i < collData.length; i++)
498 collBox.addItem(collData[i]);
499 collBox.setSelectedItem(collData[0]);
500
501 this.serviceBox.removeAllItems();
502 ServiceData[] serviceData = collData[0].getServiceList(
503 executableServicesOnly);
504 for(int i = 0; i < serviceData.length; i++)
505 serviceBox.addItem(serviceData[i]);
506 serviceBox.setSelectedIndex(0); // whichever might be the first
507 }
508
509 /** Given an XML response string, returns the root document (element)
510 * representing its DOM form.
511 * @param response - the XML response string to be converted into
512 * its DOM form
513 * @return the response as an XML DOM Element */
514 protected Element getResponseAsDOM(String debugPrefix, String response) {
515 if(response == null) // will not be the case, because an empty
516 return null; // response message will be sent instead
517
518 // First let's print out the response to the LOG
519 // Note that the response is XML and will start on new line already
520 LOG.debug(debugPrefix + response);
521 // The following line when uncommented will output all response
522 // msgs coming back from the web services
523 //System.out.println(debugPrefix + response);
524
525 // At present, assuming response is okay - later need to first look
526 // for element <error> and process this meaningfully before returning
527 // XML DOM version of response. Example error:
528 // <message>
529 // <response from="infomine/TextQuery" type="process">
530 // <documentNodeList />
531 // <error type="other">IOException during connection to http://infominehelper.ucr.edu/cgi-bin/canned_search?theme=gsdl3&amp;query=water&amp;fields=kw,au,su,ti,de,fu,&amp;no_of_records_per_page=10&amp;start_page_no=1: java.net.ConnectException: Connection refused</error>
532 // </response>
533 // </message>
534
535 Element message = null;
536 try{
537 // turn the String xml response into a DOM tree:
538 DocumentBuilder builder
539 = DocumentBuilderFactory.newInstance().newDocumentBuilder();
540 Document doc
541 = builder.parse(new InputSource(new StringReader(response)));
542 message = doc.getDocumentElement();
543 } catch(Exception e){
544 if(response == null) {
545 response = "";
546 }
547 JOptionPane.showMessageDialog(this,
548 "An error occurred while trying to parse the response:\n"
549 + response
550 + "\nException message:\n" + e.getMessage(),
551 "Parse error", JOptionPane.ERROR_MESSAGE);
552 e.printStackTrace();
553 }
554
555 NodeList errorList = message.getElementsByTagName(GSXML.ERROR_ELEM);
556 if(errorList.getLength() == 0)
557 return message;
558
559 // else we have one (or more?) error(s):
560 Element error = (Element)errorList.item(0);
561 String errorMessage = ParseUtil.getBodyTextValue(error);
562 String errorType = error.getAttribute("type");
563
564 // no need to display error message if it was a query that was
565 // just executed and no results were found
566 if(//errorType.equals(GSXML.ERROR_TYPE_SYNTAX) &&
567 errorMessage.toLowerCase().indexOf("no documentnode found") != -1)
568 {
569 // if we're in here, we know that a search/query was performed
570 // That means we are dealing with the searchResultsDisplay
571 // and can clear it.
572 this.searchResultsDisplay.clear();
573 this.searchSummary.setText("");
574 }
575 else { // display error message
576 final int numLetters = 60; // number of letters per line
577 String[] words = errorMessage.split(" "); //get the individual words
578 String line = words[0];
579 errorMessage = "";
580 // go through all the words and build up lines where none is
581 // longer than the specified number of letters
582 for(int i = 1; i < words.length; i++) { // append the word
583 // and a space to the same line unless the line is too long
584 if(numLetters > line.length() + words[i].length()) {
585 if(line.equals(""))
586 line = words[i];
587 else line = line + " " + words[i];
588
589 // check if line is full after adding new word
590 if(line.length() == numLetters) { // finish the line
591 errorMessage = errorMessage + line + "\n";
592 line = "";
593 }
594 } // else the word has to go on the next line
595 else { //finish the line and move on:
596 errorMessage = errorMessage + line + "\n";
597 line = words[i];
598 }
599 if(i == (words.length-1)) { // extra word on last line
600 errorMessage = errorMessage + line;
601 }
602 }
603 LOG.debug("errorMessage:\n" + errorMessage);
604 JOptionPane.showMessageDialog(this, errorMessage, "Error: "+errorType,
605 JOptionPane.ERROR_MESSAGE);
606 }
607 return message;
608 }
609
610 /** Event handler for actionEvents such as a change in the active digital
611 * library, a collection being selected in the dropdown box or the collection
612 * -Info button being pressed, or a service being selected in the service
613 * dropdown box (browsing or searching). */
614 public void actionPerformed(ActionEvent e) {
615 if(e.getSource() == dlChooser) {
616 // We need to change to a Wait cursor while we do the switch
617 Container c = this.getContentPane();
618 c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
619
620 boolean success = true;
621 // Change the dlAPIA handle to the chosen digital library,
622 // instantiating DigitalLibraryServicesAPIA objects when necessary:
623 // if they haven't yet been created
624 switch(dlChooser.getSelectedIndex())
625 {
626 case GREENSTONE:
627 if(this.greenstoneDL == null) {
628 success = createGreenstoneDLConnection();
629 }
630 // no need to do anything if the user chose
631 // the same DL again
632 if(success && dlAPIA!=this.greenstoneDL) {
633 // current DL not already set to greenstoneDL
634 // so set it
635 this.changeDL(greenstoneDL);
636 }
637 break;
638 case FEDORA:
639 if(this.fedoraDL == null) {
640 success = createFedoraDLConnection();
641 }
642 // no need to do anything if the user chose
643 // the same DL again
644 if(success && dlAPIA!=this.fedoraDL) {
645 // current DL not already set to fedoraDL
646 // so set it
647 this.changeDL(fedoraDL);
648 }
649 break;
650 case SELECT:
651 default:
652 this.changeDL(null); // no dl, so default colours
653 break;
654 }
655 // set the cursor back
656 c.setCursor(Cursor.getDefaultCursor());
657 } else if(e.getSource() == collBox) {
658 CollectionData collDataEl
659 = (CollectionData)collBox.getSelectedItem();
660
661 if(collDataEl == null) // need to check for this here, because
662 return; // removing all items from collBox fires actionPerformed
663
664 // display the collection's full name
665 this.collectionNameField.setText(collDataEl.getDisplayName());
666 this.collectionNameField.setCaretPosition(0);
667
668 // display the services in the selected collection
669 serviceBox.removeAllItems();
670 ServiceData[] servicelist = collDataEl.getServiceList(
671 executableServicesOnly);
672 for(int i = 0; i < servicelist.length; i++)
673 serviceBox.addItem(servicelist[i]);
674
675 } else if(e.getSource() == serviceBox) {
676 ServiceData selService = (ServiceData)serviceBox.getSelectedItem();
677 if(selService == null || selService.type == null)
678 return; // nothing valid chosen
679
680 if(selService.type.equals(GSXML.SERVICE_TYPE_QUERY)) {
681 // Make sure we can't accidentally work with already deallocated
682 // data objects in the browsePanel
683 browsePanel.clear();
684 searchSummary.setText(""); // empty any text in the search summary
685
686 this.activity = SEARCHING; // ensures the components are
687 // displayed and shown again, whereas a call to repaint()
688 // did not do the trick
689
690 tabbedPane.setSelectedComponent(this.queryPanel);
691
692 // send off a describe request to the service
693 // in that collection
694 CollectionData selColl
695 = (CollectionData)collBox.getSelectedItem();
696 colName = selColl.name;
697 serviceName = selService.name;
698
699 String response = dlAPIA.describeCollectionService(
700 colName, serviceName);
701 Element serviceResponseXML = getResponseAsDOM(
702 "DescribeCollectionService "+colName+"/"+serviceName,
703 response);
704
705 // generate the appropriate query form as per how the
706 // query has described itself
707 queryPanel.formFromQueryServiceDescribe(serviceResponseXML);
708 } else if(selService.type.equals(GSXML.SERVICE_TYPE_BROWSE)) {
709 LOG.debug("Browse option chosen");
710 this.searchResultsDisplay.clear();
711 this.searchSummary.setText("");
712 this.queryPanel.clear(); // can't do any searching either
713 this.activity = BROWSING;
714 tabbedPane.setSelectedComponent(this.browsePanel);
715
716 CollectionData selColl
717 = (CollectionData)collBox.getSelectedItem();
718 colName = selColl.name;
719 serviceName = selService.name;
720
721 // first send a request for the browse service's metadata
722 // see manual pp.37 and 48
723 String response = dlAPIA.describeCollectionService(
724 colName, serviceName);
725 Element responseMsg = getResponseAsDOM(
726 colName+"/"+serviceName+":", response);
727 browsePanel.displayBrowseOptions(responseMsg);
728 } else { // clicked on non-query, non-browse option, remove form
729 queryPanel.clear();
730 }
731 } else if(e.getSource() == collInfoButton){
732 CollectionData collDataEl
733 = (CollectionData)collBox.getSelectedItem();
734
735 // Create the HTML viewing pane.
736 JEditorPane information = new JEditorPane();
737 information.setEditable(false);
738 information.setContentType("text/html");
739 information.setText(collDataEl.info());
740
741 // Display the dialog with the information in a scrollpane
742 JDialog dialog = new JDialog(this, collDataEl.toString());
743 dialog.getContentPane().add(new JScrollPane(information));
744 dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE); // not EXIT!
745 dialog.pack();
746 dialog.setSize(400, 300);
747 dialog.setVisible(true);
748 }
749 else if(e.getSource() == setProxySettings) { // proxy settings button
750 // Sthe proxy settings as given in the fields
751 String proxyHost = this.proxyhostField.getText();
752 String proxyPort = this.proxyportField.getText();
753 if(proxyHost.equals("") && proxyPort.equals("")) {
754 return;
755 } else {
756 // Handle proxy settings
757 // (don't need to write to propertiesFile, since proxy
758 // settings are System properties)
759 java.util.Properties systemSettings = System.getProperties();
760 systemSettings.put("http.proxyHost", proxyHost);
761 systemSettings.put("http.proxyPort", proxyPort);
762 // give the hosts for which no proxies are required:
763 systemSettings.put("http.nonProxyHosts",
764 this.nonProxyHostNamesField.getText());
765 System.setProperties(systemSettings);
766 }
767 }
768 }
769
770 /** Called by the instance of the QueryForm class when the user has
771 * pressed the queryPanel's search button to execute a query. Based on
772 * the user-entered values (stored in the parameter HashMap) this
773 * method will call Greenstone to process the query.
774 * @param nameValParamsMap - the query form control names with the values
775 * the user has entered for them. Example, the pair (fqv, the query string),
776 * where fqv is the form control name for the query term. */
777 public void performSearch(HashMap nameValParamsMap) {
778 //Container c = this.getContentPane();
779 //c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
780 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
781
782 // (1) Now let's process this query request - passing the
783 // currently selected collection and service:
784 String responseXML = dlAPIA.query(
785 colName, serviceName, nameValParamsMap);
786
787 // (2) The search results: document identifiers are returned,
788 // use these to construct a QueryResponseData object
789 Element responseMessage
790 = getResponseAsDOM("Search response:", responseXML);
791 queryResponseObj.setResponseData(responseMessage);
792 LOG.debug(queryResponseObj);
793
794 DocumentNodeData[] doclist = queryResponseObj.getDocumentNodeList();
795
796 // (3) To retrieve Title metadata, need to call with "Title"!!!!
797 // (1st letter in caps)
798 // Retrieving just metadata name=Title for ALL docs (=all docIDs)
799 String metaResponseXML = dlAPIA.retrieveTitleMetadata(this.colName,
800 queryResponseObj.getDocumentNodeIDs());
801 Element metaResponse
802 = getResponseAsDOM("Meta response:", metaResponseXML);
803 queryResponseObj.setMetadataForDocuments(metaResponse);
804
805 // doclist should now have been updated with title metadata for
806 // each doc (docNode)
807 //for(int i = 0; i < doclist.length; i++)
808 //LOG.debug(doclist[i].show());
809
810 // (4) Display this in the search results TreeView
811 tabbedPane.setSelectedComponent(searchPanel);
812 searchResultsDisplay.setResults(doclist);
813 searchSummary.setText(queryResponseObj.toString());
814 searchResultsDisplay.validate();
815 this.searchPanel.validate();
816 this.searchPanel.repaint();
817
818 // set the cursor back
819 //c.setCursor(Cursor.getDefaultCursor());
820 this.setCursor(Cursor.getDefaultCursor());
821 }
822
823
824 /* SEARCH RELATED METHODS */
825 /** Performs a docMetadataRetrieve message request for the docNode
826 * iff the metadata for that docNode is not already set.
827 * @param docNode is the DocumentNodeData object for which all the
828 * metadata is to be retrieved. */
829 public void retrieveAllMetadataFor(DocumentNodeData docNode) {
830 // Lazy retrieval: only retrieve metadata of docNode when required
831 // and if metadata not already set
832 if(docNode.getMetadataList() == null
833 || docNode.getMetadataList().size() <= 1)
834 { // not set yet or only title set,
835 // so do a docMetadataRetrieve for the docNode (all
836 // metadata fields retrieved):
837 String metaResponseXML = dlAPIA.retrieveDocumentMetadata(
838 this.colName, new String[] { docNode.nodeID });
839 Element metaResponse
840 = getResponseAsDOM("Meta response", metaResponseXML);
841
842 // Set the metadata for the docNode
843 if(this.activity == SEARCHING)
844 queryResponseObj.setMetadataForDocuments(metaResponse);
845 else if(this.activity == BROWSING)
846 browseResponseObject.setMetadataForDocuments(metaResponse);
847 } //else docNode's list of metadata already set
848 }
849
850 /** Performs a docMetadataRetrieve message request for the docNode
851 * iff the nodeContent for that docNode is not already set.
852 * @param docNode is the DocumentNodeData object for which the content
853 * is to be retrieved. */
854 public void retrieveContentFor(DocumentNodeData docNode) {
855 // Lazy retrieval: only retrieve content when required.
856 // If it is not yet set, then we do the retrieval, else
857 // our work here is done
858 if(docNode.getContent() == null) { // not set yet, so
859 // retrieve the content for the docNode
860 String contentResponseXML = dlAPIA.retrieveDocumentContent(
861 this.colName, new String[] { docNode.nodeID });
862 Element contentResponse = getResponseAsDOM(
863 "Content response:", contentResponseXML);
864 // probably when infomine is down
865 // Set the content for the docNode
866 if(this.activity==SEARCHING)
867 queryResponseObj.setContentForDocs(contentResponse);
868 else if(this.activity==BROWSING)
869 browseResponseObject.setContentForDocs(contentResponse);
870 }
871 }
872
873 /** Performs a structureRetrieve and title metadata retrieve message-
874 * request for the docNode, but only iff the structure and title for
875 * that docNode is not already set.
876 * @param docNode is the DocumentNodeData object for which the title
877 * is to be retrieved along with the titles of all its descendants. */
878 public void retrieveTitledStructureFor(DocumentNodeData docNode) {
879 DocumentNodeData root = docNode.getRoot();
880 if(root == null) { //then its structure has not yet been set
881 // do a structure retrieve for this document:
882 String structureResponseXML = dlAPIA.retrieveDocumentStructure(
883 this.colName, new String[] { docNode.nodeID });
884 Element structureResponse = getResponseAsDOM(
885 "STRUCTURE: ", structureResponseXML);
886
887 // Get the nodeStructure of this docNode, find its root and
888 // from there set all the descendents
889 // Instead of the following 2 statements can also do:
890 // queryResponseObj.setStructureForDocs(structureResponse);
891
892 Element nodeStructure = ParseUtil.getFirstDescElementCalled(
893 structureResponse, GSXML.NODE_STRUCTURE_ELEM);
894
895 // now we set the root and its descendents using whatever
896 ResponseData responseObject = browseResponseObject;
897 // true if(this.activity == BROWSING)
898 if(this.activity == SEARCHING)
899 responseObject = queryResponseObj;
900
901 root = docNode.setDescendentsOfRootNode(nodeStructure,
902 responseObject.getIDToNodeMapping());
903
904 // Now get only the DocumentNodeData elements in the root's
905 // descendents whose titles have not yet been set
906 String[] setTitleForTheseNodeIDs = root.getDescNodeIDsAsList(true);
907 // Will be null if all titles already set! But this should not be
908 // the case if we have just set the descendents of the root node
909 if(setTitleForTheseNodeIDs != null) {
910 // Retrieve the title metadata for these and set their titles
911 // with the response:
912 String titleMetaResponseXML = dlAPIA.retrieveTitleMetadata(
913 this.colName, setTitleForTheseNodeIDs);
914 Element titleMetaResponse = getResponseAsDOM(
915 "titleMetaResponseXML:", titleMetaResponseXML);
916
917 // Use whatever responseObject is active to set the metadata
918 responseObject.setMetadataForDocuments(titleMetaResponse);
919 }
920 }
921 }
922
923 /* BROWSE RELATED METHODS */
924 /** Given a ClassifierData object indicating what browsing category
925 * was chosen, this method will retrieve all its descendants in the
926 * hierarchy. Only the top-level descendents (children) of the
927 * classification will be set initially.
928 * @param classifier is the ClassifierData object whose children are
929 * to be retrieved. */
930 public void doBrowse(ClassifierData classifier) {
931 String response = dlAPIA.retrieveBrowseStructure(
932 this.colName, this.serviceName, classifier.name);
933
934 browseResponseObject.clear();
935 Element responseMsgTag = this.getResponseAsDOM(
936 "browseResponse:", response);
937 browseResponseObject.setResponseData(responseMsgTag); //classifier.name
938
939 LOG.debug(browseResponseObject.show());
940
941 this.browsePanel.displayBrowseResults(browseResponseObject,
942 classifier.displayName);
943 this.browsePanel.validate();
944 }
945
946 /** Given a ClassifierNodeData object for browsing, this method will
947 * set the classNode's children (previously retrieved) and retrieve
948 * all the children's metadata including the title.
949 * @param classNode is the ClassifierNodeData object whose title is to
950 * be retrieved along with the titles for its children. */
951 public void retrieveTitledStructureFor(ClassifierNodeData classNode) {
952 // we will only be setting the children in this method and retrieving
953 // the names for them, as it concerns a classifierNodeData (and not
954 // a documentNodeData).
955
956 classNode.setChildren(browseResponseObject.getIDToNodeMapping());
957
958 //String rootID = browseResponseObject.getRootClassifier().nodeID;
959
960 NodeData[] children = classNode.getChildren();
961 Vector docNodeChildren = new Vector(children.length);
962 Vector classNodeChildren = new Vector(children.length);
963 //String nodeIDs[] = new String[children.length];
964 for(int i = 0; i < children.length; i++) {
965 // don't bother setting the metadata if it was
966 // already set (check for empty metadata
967 if(children[i].getMetadataList() == null) {
968 if(children[i] instanceof ClassifierNodeData)
969 classNodeChildren.add(children[i]);
970 else if(children[i] instanceof DocumentNodeData)
971 docNodeChildren.add(children[i]);
972 }
973 }
974 String[] nodeIDs = null;
975
976 // First set the metadata for any classifiers:
977 if(classNodeChildren.size() > 0) {
978 nodeIDs = new String[classNodeChildren.size()];
979 for(int i = 0; i < nodeIDs.length; i++)
980 nodeIDs[i] = ((NodeData)classNodeChildren.get(i)).nodeID;
981
982 // let's just retrieve all the metadata - need to display it
983 // soon anyway
984 String response = dlAPIA.retrieveBrowseMetadata(this.colName,
985 this.serviceName, nodeIDs);
986 Element responseXML = this.getResponseAsDOM(
987 "MetadataRetrieve response: ", response);
988 browseResponseObject.setMetadataForClassifiers(responseXML);
989
990 for(int i = 0; i < nodeIDs.length; i++)
991 LOG.debug("Title: "
992 + ((NodeData)classNodeChildren.get(i)).getTitle());
993 }
994
995 nodeIDs = null;
996 // Now set the metadata for any documentNodes for which metadata
997 // has not been set yet
998 if(docNodeChildren.size() > 0) {
999 nodeIDs = new String[docNodeChildren.size()];
1000 for(int i = 0; i < nodeIDs.length; i++)
1001 nodeIDs[i] = ((NodeData)docNodeChildren.get(i)).nodeID;
1002
1003 // let's just retrieve all the metadata - need to display it
1004 // soon anyway
1005 String response = dlAPIA.retrieveDocumentMetadata(
1006 this.colName, nodeIDs);
1007 Element responseXML = this.getResponseAsDOM(
1008 "MetadataRetrieve response: ", response);
1009 browseResponseObject.setMetadataForDocuments(responseXML);
1010
1011 for(int i = 0; i < nodeIDs.length; i++)
1012 LOG.debug("Title: "
1013 + ((NodeData)docNodeChildren.get(i)).getTitle());
1014 }
1015 }
1016
1017 /** @return the baseURL of the active digital library interface object */
1018 public String getBaseURL() {
1019 return this.dlAPIA.getAssocFileBaseURL();
1020 }
1021
1022 /** @return the filepath of the documentNode in the active digital library */
1023 public String getFilePath(DocumentNodeData docNode) {
1024 return this.dlAPIA.getAssocFileBaseURL(docNode);
1025 }
1026
1027 /**
1028 * Changes the background colours of the client's user interface
1029 * based on what the active digital library is.
1030 * See files greenstone3/gli/classes/xml/config.xml and
1031 * greenstone3/gli/src/org/greenstone/gatherer/Configuration.java
1032 * @param DL indicates whether the active digital library is Greenstone or
1033 * Fedora (or neither, in which case the GUI defaults to the usual grey).
1034 * @see <a href="http://www.java2s.com/Code/Java/Swing-JFC/UIManagerresourcestotweakthelookofapplications.htm">Java UIManager tutorial</a>
1035 */
1036 public void changeUIColour(int DL)
1037 {
1038 ColourCombo.setColour(DL);
1039 ColourCombo.changeColor(this.collBox);
1040 ColourCombo.changeColor(this.collectionNameField);
1041 ColourCombo.changeColor(this.serviceBox);
1042
1043 // change anonymous super panels' colours:
1044 ColourCombo.changeAncestorColor(this.collBox);
1045 ColourCombo.changeAncestorColor(this.serviceBox);
1046
1047 // change tab pane panels and their colouring
1048 this.queryPanel.changeUIColour();
1049 this.browsePanel.changeUIColour();
1050 this.searchResultsDisplay.changeUIColour();
1051 }
1052
1053 /** The main method of the GS3 java-client application. It instantiates
1054 * an instance of the GS3JavaClient main window and makes it visible. */
1055 public static void main(String[] args) {
1056 // set it up for logging
1057 final String logPropsFile = "log4j.properties";
1058 PropertyConfigurator.configure(logPropsFile);
1059
1060 GS3JavaClient client = new GS3JavaClient();
1061 client.pack();
1062 // make the main window fullscreen
1063 client.setSize(Toolkit.getDefaultToolkit().getScreenSize());
1064 client.setVisible(true);
1065 client.setDefaultCloseOperation(EXIT_ON_CLOSE);
1066 }
1067}
Note: See TracBrowser for help on using the repository browser.