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

Last change on this file since 26174 was 26174, checked in by ak19, 12 years ago

Handling special case in search form control generation, where no controls but pure labels are specified.

File size: 42.5 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 + "\n" + response);
521 // The following line when uncommented will output all response
522 // msgs coming back from the web services
523 //System.out.println(debugPrefix + "\n" + 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 LOG.error("**** serviceResponse XML:" + response);
702 System.err.println("**** serviceResponse XML:" + response);
703
704 Element serviceResponseXML = getResponseAsDOM(
705 "DescribeCollectionService "+colName+"/"+serviceName,
706 response);
707
708 // generate the appropriate query form as per how the
709 // query has described itself
710 queryPanel.formFromQueryServiceDescribe(serviceResponseXML);
711 } else if(selService.type.equals(GSXML.SERVICE_TYPE_BROWSE)) {
712 LOG.debug("Browse option chosen");
713 this.searchResultsDisplay.clear();
714 this.searchSummary.setText("");
715 this.queryPanel.clear(); // can't do any searching either
716 this.activity = BROWSING;
717 tabbedPane.setSelectedComponent(this.browsePanel);
718
719 CollectionData selColl
720 = (CollectionData)collBox.getSelectedItem();
721 colName = selColl.name;
722 serviceName = selService.name;
723
724 // first send a request for the browse service's metadata
725 // see manual pp.37 and 48
726 String response = dlAPIA.describeCollectionService(
727 colName, serviceName);
728 Element responseMsg = getResponseAsDOM(
729 colName+"/"+serviceName+":", response);
730 browsePanel.displayBrowseOptions(responseMsg);
731 } else { // clicked on non-query, non-browse option, remove form
732 queryPanel.clear();
733 }
734 } else if(e.getSource() == collInfoButton){
735 CollectionData collDataEl
736 = (CollectionData)collBox.getSelectedItem();
737
738 // Create the HTML viewing pane.
739 JEditorPane information = new JEditorPane();
740 information.setEditable(false);
741 information.setContentType("text/html");
742 information.setText(collDataEl.info());
743
744 // Display the dialog with the information in a scrollpane
745 JDialog dialog = new JDialog(this, collDataEl.toString());
746 dialog.getContentPane().add(new JScrollPane(information));
747 dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE); // not EXIT!
748 dialog.pack();
749 dialog.setSize(400, 300);
750 dialog.setVisible(true);
751 }
752 else if(e.getSource() == setProxySettings) { // proxy settings button
753 // Sthe proxy settings as given in the fields
754 String proxyHost = this.proxyhostField.getText();
755 String proxyPort = this.proxyportField.getText();
756 if(proxyHost.equals("") && proxyPort.equals("")) {
757 return;
758 } else {
759 // Handle proxy settings
760 // (don't need to write to propertiesFile, since proxy
761 // settings are System properties)
762 java.util.Properties systemSettings = System.getProperties();
763 systemSettings.put("http.proxyHost", proxyHost);
764 systemSettings.put("http.proxyPort", proxyPort);
765 // give the hosts for which no proxies are required:
766 systemSettings.put("http.nonProxyHosts",
767 this.nonProxyHostNamesField.getText());
768 System.setProperties(systemSettings);
769 }
770 }
771 }
772
773 /** Called by the instance of the QueryForm class when the user has
774 * pressed the queryPanel's search button to execute a query. Based on
775 * the user-entered values (stored in the parameter HashMap) this
776 * method will call Greenstone to process the query.
777 * @param nameValParamsMap - the query form control names with the values
778 * the user has entered for them. Example, the pair (fqv, the query string),
779 * where fqv is the form control name for the query term. */
780 public void performSearch(HashMap nameValParamsMap) {
781 //Container c = this.getContentPane();
782 //c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
783 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
784
785 // (1) Now let's process this query request - passing the
786 // currently selected collection and service:
787 String responseXML = dlAPIA.query(
788 colName, serviceName, nameValParamsMap);
789
790 // (2) The search results: document identifiers are returned,
791 // use these to construct a QueryResponseData object
792 Element responseMessage
793 = getResponseAsDOM("Search response:", responseXML);
794 queryResponseObj.setResponseData(responseMessage);
795 LOG.debug(queryResponseObj);
796
797 DocumentNodeData[] doclist = queryResponseObj.getDocumentNodeList();
798
799 // (3) To retrieve Title metadata, need to call with "Title"!!!!
800 // (1st letter in caps)
801 // Retrieving just metadata name=Title for ALL docs (=all docIDs)
802 String metaResponseXML = dlAPIA.retrieveTitleMetadata(this.colName,
803 queryResponseObj.getDocumentNodeIDs());
804 Element metaResponse
805 = getResponseAsDOM("Meta response:", metaResponseXML);
806 queryResponseObj.setMetadataForDocuments(metaResponse);
807
808 // doclist should now have been updated with title metadata for
809 // each doc (docNode)
810 //for(int i = 0; i < doclist.length; i++)
811 //LOG.debug(doclist[i].show());
812
813 // (4) Display this in the search results TreeView
814 tabbedPane.setSelectedComponent(searchPanel);
815 searchResultsDisplay.setResults(doclist);
816 searchSummary.setText(queryResponseObj.toString());
817 searchResultsDisplay.validate();
818 this.searchPanel.validate();
819 this.searchPanel.repaint();
820
821 // set the cursor back
822 //c.setCursor(Cursor.getDefaultCursor());
823 this.setCursor(Cursor.getDefaultCursor());
824 }
825
826
827 /* SEARCH RELATED METHODS */
828 /** Performs a docMetadataRetrieve message request for the docNode
829 * iff the metadata for that docNode is not already set.
830 * @param docNode is the DocumentNodeData object for which all the
831 * metadata is to be retrieved. */
832 public void retrieveAllMetadataFor(DocumentNodeData docNode) {
833 // Lazy retrieval: only retrieve metadata of docNode when required
834 // and if metadata not already set
835 if(docNode.getMetadataList() == null
836 || docNode.getMetadataList().size() <= 1)
837 { // not set yet or only title set,
838 // so do a docMetadataRetrieve for the docNode (all
839 // metadata fields retrieved):
840 String metaResponseXML = dlAPIA.retrieveDocumentMetadata(
841 this.colName, new String[] { docNode.nodeID }, new String[] {"all"});
842 Element metaResponse
843 = getResponseAsDOM("Meta response", metaResponseXML);
844
845 // Set the metadata for the docNode
846 if(this.activity == SEARCHING)
847 queryResponseObj.setMetadataForDocuments(metaResponse);
848 else if(this.activity == BROWSING)
849 browseResponseObject.setMetadataForDocuments(metaResponse);
850 } //else docNode's list of metadata already set
851 }
852
853 /** Performs a docMetadataRetrieve message request for the docNode
854 * iff the nodeContent for that docNode is not already set.
855 * @param docNode is the DocumentNodeData object for which the content
856 * is to be retrieved. */
857 public void retrieveContentFor(DocumentNodeData docNode) {
858 // Lazy retrieval: only retrieve content when required.
859 // If it is not yet set, then we do the retrieval, else
860 // our work here is done
861 if(docNode.getContent() == null) { // not set yet, so
862 // retrieve the content for the docNode
863 String contentResponseXML = dlAPIA.retrieveDocumentContent(
864 this.colName, new String[] { docNode.nodeID });
865 Element contentResponse = getResponseAsDOM(
866 "Content response:", contentResponseXML);
867 // probably when infomine is down
868 // Set the content for the docNode
869 if(this.activity==SEARCHING)
870 queryResponseObj.setContentForDocs(contentResponse);
871 else if(this.activity==BROWSING)
872 browseResponseObject.setContentForDocs(contentResponse);
873 }
874 }
875
876 /** Performs a structureRetrieve and title metadata retrieve message-
877 * request for the docNode, but only iff the structure and title for
878 * that docNode is not already set.
879 * @param docNode is the DocumentNodeData object for which the title
880 * is to be retrieved along with the titles of all its descendants. */
881 public void retrieveTitledStructureFor(DocumentNodeData docNode) {
882 DocumentNodeData root = docNode.getRoot();
883 if(root == null) { //then its structure has not yet been set
884 // do a structure retrieve for this document:
885 String structureResponseXML =
886 dlAPIA.retrieveDocumentStructure(this.colName, new String[] { docNode.nodeID },
887 new String[] {"descendants"}, new String[]{""});
888 Element structureResponse = getResponseAsDOM(
889 "STRUCTURE: ", structureResponseXML);
890
891 // Get the nodeStructure of this docNode, find its root and
892 // from there set all the descendents
893 // Instead of the following 2 statements can also do:
894 // queryResponseObj.setStructureForDocs(structureResponse);
895
896 Element nodeStructure = ParseUtil.getFirstDescElementCalled(
897 structureResponse, GSXML.NODE_STRUCTURE_ELEM);
898
899 // now we set the root and its descendents using whatever
900 ResponseData responseObject = browseResponseObject;
901 // true if(this.activity == BROWSING)
902 if(this.activity == SEARCHING)
903 responseObject = queryResponseObj;
904
905 root = docNode.setDescendentsOfRootNode(nodeStructure,
906 responseObject.getIDToNodeMapping());
907
908 // Now get only the DocumentNodeData elements in the root's
909 // descendents whose titles have not yet been set
910 String[] setTitleForTheseNodeIDs = root.getDescNodeIDsAsList(true);
911 // Will be null if all titles already set! But this should not be
912 // the case if we have just set the descendents of the root node
913 if(setTitleForTheseNodeIDs != null) {
914 // Retrieve the title metadata for these and set their titles
915 // with the response:
916 String titleMetaResponseXML = dlAPIA.retrieveTitleMetadata(
917 this.colName, setTitleForTheseNodeIDs);
918 Element titleMetaResponse = getResponseAsDOM(
919 "titleMetaResponseXML:", titleMetaResponseXML);
920
921 // Use whatever responseObject is active to set the metadata
922 responseObject.setMetadataForDocuments(titleMetaResponse);
923 }
924 }
925 }
926
927 /* BROWSE RELATED METHODS */
928 /** Given a ClassifierData object indicating what browsing category
929 * was chosen, this method will retrieve all its descendants in the
930 * hierarchy. Only the top-level descendents (children) of the
931 * classification will be set initially.
932 * @param classifier is the ClassifierData object whose children are
933 * to be retrieved. */
934 public void doBrowse(ClassifierData classifier) {
935 String[] classifierNames = { classifier.name };
936 String response = dlAPIA.retrieveBrowseStructure(
937 this.colName, this.serviceName, classifierNames,
938 new String[]{"entire"}, new String[]{""}); // structure and info
939
940 browseResponseObject.clear();
941 Element responseMsgTag = this.getResponseAsDOM(
942 "browseResponse:", response);
943 browseResponseObject.setResponseData(responseMsgTag); //classifier.name
944
945 LOG.debug(browseResponseObject.show());
946
947 this.browsePanel.displayBrowseResults(browseResponseObject,
948 classifier.displayName);
949 this.browsePanel.validate();
950 }
951
952 /** Given a ClassifierNodeData object for browsing, this method will
953 * set the classNode's children (previously retrieved) and retrieve
954 * all the children's metadata including the title.
955 * @param classNode is the ClassifierNodeData object whose title is to
956 * be retrieved along with the titles for its children. */
957 public void retrieveTitledStructureFor(ClassifierNodeData classNode) {
958 // we will only be setting the children in this method and retrieving
959 // the names for them, as it concerns a classifierNodeData (and not
960 // a documentNodeData).
961
962 classNode.setChildren(browseResponseObject.getIDToNodeMapping());
963
964 //String rootID = browseResponseObject.getRootClassifier().nodeID;
965
966 NodeData[] children = classNode.getChildren();
967 Vector docNodeChildren = new Vector(children.length);
968 Vector classNodeChildren = new Vector(children.length);
969 //String nodeIDs[] = new String[children.length];
970 for(int i = 0; i < children.length; i++) {
971 // don't bother setting the metadata if it was
972 // already set (check for empty metadata
973 if(children[i].getMetadataList() == null) {
974 if(children[i] instanceof ClassifierNodeData)
975 classNodeChildren.add(children[i]);
976 else if(children[i] instanceof DocumentNodeData)
977 docNodeChildren.add(children[i]);
978 }
979 }
980 String[] nodeIDs = null;
981
982 // First set the metadata for any classifiers:
983 if(classNodeChildren.size() > 0) {
984 nodeIDs = new String[classNodeChildren.size()];
985 for(int i = 0; i < nodeIDs.length; i++)
986 nodeIDs[i] = ((NodeData)classNodeChildren.get(i)).nodeID;
987
988 // let's just retrieve all the metadata - need to display it
989 // soon anyway
990 String response = dlAPIA.retrieveBrowseMetadata(this.colName, this.serviceName,
991 nodeIDs, new String[]{"all"});
992 Element responseXML = this.getResponseAsDOM(
993 "MetadataRetrieve response: ", response);
994 browseResponseObject.setMetadataForClassifiers(responseXML);
995
996 for(int i = 0; i < nodeIDs.length; i++)
997 LOG.debug("Title: "
998 + ((NodeData)classNodeChildren.get(i)).getTitle());
999 }
1000
1001 nodeIDs = null;
1002 // Now set the metadata for any documentNodes for which metadata
1003 // has not been set yet
1004 if(docNodeChildren.size() > 0) {
1005 nodeIDs = new String[docNodeChildren.size()];
1006 for(int i = 0; i < nodeIDs.length; i++)
1007 nodeIDs[i] = ((NodeData)docNodeChildren.get(i)).nodeID;
1008
1009 // let's just retrieve all the metadata - need to display it
1010 // soon anyway
1011 String response = dlAPIA.retrieveDocumentMetadata(this.colName, nodeIDs,
1012 new String[] {"all"});
1013 Element responseXML = this.getResponseAsDOM(
1014 "MetadataRetrieve response: ", response);
1015 browseResponseObject.setMetadataForDocuments(responseXML);
1016
1017 for(int i = 0; i < nodeIDs.length; i++)
1018 LOG.debug("Title: "
1019 + ((NodeData)docNodeChildren.get(i)).getTitle());
1020 }
1021 }
1022
1023 /** @return the baseURL of the active digital library interface object */
1024 public String getBaseURL() {
1025 return this.dlAPIA.getAssocFileBaseURL();
1026 }
1027
1028 /** @return the filepath of the documentNode in the active digital library */
1029 public String getFilePath(DocumentNodeData docNode) {
1030 return this.dlAPIA.getAssocFileBaseURL(docNode);
1031 }
1032
1033 /**
1034 * Changes the background colours of the client's user interface
1035 * based on what the active digital library is.
1036 * See files greenstone3/gli/classes/xml/config.xml and
1037 * greenstone3/gli/src/org/greenstone/gatherer/Configuration.java
1038 * @param DL indicates whether the active digital library is Greenstone or
1039 * Fedora (or neither, in which case the GUI defaults to the usual grey).
1040 * @see <a href="http://www.java2s.com/Code/Java/Swing-JFC/UIManagerresourcestotweakthelookofapplications.htm">Java UIManager tutorial</a>
1041 */
1042 public void changeUIColour(int DL)
1043 {
1044 ColourCombo.setColour(DL);
1045 ColourCombo.changeColor(this.collBox);
1046 ColourCombo.changeColor(this.collectionNameField);
1047 ColourCombo.changeColor(this.serviceBox);
1048
1049 // change anonymous super panels' colours:
1050 ColourCombo.changeAncestorColor(this.collBox);
1051 ColourCombo.changeAncestorColor(this.serviceBox);
1052
1053 // change tab pane panels and their colouring
1054 this.queryPanel.changeUIColour();
1055 this.browsePanel.changeUIColour();
1056 this.searchResultsDisplay.changeUIColour();
1057 }
1058
1059 /** The main method of the GS3 java-client application. It instantiates
1060 * an instance of the GS3JavaClient main window and makes it visible. */
1061 public static void main(String[] args) {
1062 // set it up for logging
1063 final String logPropsFile = "log4j.properties";
1064 PropertyConfigurator.configure(logPropsFile);
1065
1066 GS3JavaClient client = new GS3JavaClient();
1067 client.pack();
1068 // make the main window fullscreen (except for on Windows,
1069 // where it disappears under the bar at the bottom)
1070 java.awt.Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
1071 if(System.getProperty("os.name").toLowerCase().contains("windows")) {
1072 d.setSize(d.getWidth()-100, d.getHeight()-100);
1073 }
1074 client.setSize(d);
1075 client.setVisible(true);
1076 client.setDefaultCloseOperation(EXIT_ON_CLOSE);
1077 }
1078}
Note: See TracBrowser for help on using the repository browser.