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

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

Exception dialog explicitly indicates failure to connect to Greenstone 3's QBRSOAPServer web services

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