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

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