/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * *

* * Author: John Thompson, Greenstone Digital Library, University of Waikato * *

* * Copyright (C) 1999 New Zealand Digital Library Project * *

* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * *

* * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *

* * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer.gui; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.tree.*; import org.greenstone.gatherer.Configuration; import org.greenstone.gatherer.DebugStream; import org.greenstone.gatherer.Dictionary; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.file.WorkspaceTree; import org.greenstone.gatherer.greenstone.LocalGreenstone; import org.greenstone.gatherer.util.StaticStrings; import org.greenstone.gatherer.util.Utility; import org.greenstone.gatherer.download.Download; import org.greenstone.gatherer.download.DownloadScrollPane; import org.greenstone.gatherer.download.ServerInfoDialog; import org.greenstone.gatherer.util.XMLTools; import org.greenstone.gatherer.cdm.*; import org.greenstone.gatherer.gui.*; import org.w3c.dom.*; import org.xml.sax.*; import org.greenstone.gatherer.GAuthenticator; /** * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.1 */ public class DownloadPane extends JPanel { static final private Dimension LABEL_SIZE = new Dimension(225, 25); static final private Dimension TREE_SIZE = new Dimension(150, 500); static final private String CONTENTS[] = { "DOWNLOAD.MODE.WebDownload", "DOWNLOAD.MODE.MediaWikiDownload", "DOWNLOAD.MODE.OAIDownload", "DOWNLOAD.MODE.ZDownload" , "DOWNLOAD.MODE.SRWDownload"}; private boolean download_button_enabled = false; private boolean ready = false; private JPanel options_pane; // TODO should use Vector to store all loaded downloads!! private DesignTree tree; private HashMap download_map; private ServerInfoDialog server_info; private JScrollPane list_scroll; private DownloadScrollPane getter; private String mode = null; private TreePath previous_path; private String proxy_url = ""; /** Main System code */ public DownloadPane() { super(); JScrollPane scrol_tmp; this.setComponentOrientation(Dictionary.getOrientation()); // TODO: Download the WDownload and the download panel fixed!! getter = new DownloadScrollPane(); getter.start(); list_scroll = getter.getDownloadJobList(); list_scroll.setComponentOrientation(Dictionary.getOrientation()); // TODO should use Vector to store all loaded downloads!! String lang = Configuration.getLanguage(); download_map = new HashMap(); download_map.put("Web", loadDownload("WebDownload",lang)); download_map.put("MediaWiki", loadDownload("MediaWikiDownload",lang)); download_map.put("OAI", loadDownload("OAIDownload",lang)); download_map.put("Z3950", loadDownload("Z3950Download",lang)); download_map.put("SRW", loadDownload("SRWDownload",lang)); // Creation tree = new DesignTree(); tree.setComponentOrientation(Dictionary.getOrientation()); options_pane = new JPanel(); options_pane.setComponentOrientation(Dictionary.getOrientation()); JButton clear_cache_button = new GLIButton(Dictionary.get("Mirroring.ClearCache"), Dictionary.get("Mirroring.ClearCache_Tooltip")); clear_cache_button.setEnabled(true); clear_cache_button.setMnemonic(KeyEvent.VK_C); JButton download_button = new GLIButton(Dictionary.get("Mirroring.Download"), Dictionary.get("Mirroring.Download_Tooltip")); download_button.setEnabled(true); download_button.setMnemonic(KeyEvent.VK_D); JButton information_button = new GLIButton(Dictionary.get("Download.ServerInformation"), Dictionary.get("Download.ServerInformation_Tooltip")); information_button.setEnabled(true); information_button.setMnemonic(KeyEvent.VK_S); JButton preferences_button = new GLIButton(Dictionary.get("Mirroring.Preferences"), Dictionary.get("Mirroring.Preferences_Tooltip")); preferences_button.setEnabled(true); preferences_button.setMnemonic(KeyEvent.VK_P); // Connect clear_cache_button.addActionListener(new ClearCacheListener()); download_button.addActionListener(new DownloadButtonListener()); preferences_button.addActionListener(new PreferencesButtonActionListener()); information_button.addActionListener(new InformationButtonActionListener()); tree.addTreeSelectionListener(new TreeListener()); // Add to Panel JPanel button_pane = new JPanel(); button_pane.setComponentOrientation(Dictionary.getOrientation()); button_pane.setLayout(new FlowLayout(FlowLayout.CENTER,20,5)); button_pane.setBorder(BorderFactory.createEtchedBorder()); button_pane.add(clear_cache_button); button_pane.add(download_button); button_pane.add(information_button); button_pane.add(preferences_button); JPanel tree_pane = new JPanel(); tree_pane.setComponentOrientation(Dictionary.getOrientation()); tree_pane.setLayout(new BorderLayout()); scrol_tmp = new JScrollPane(tree); scrol_tmp.setComponentOrientation(Dictionary.getOrientation()); tree_pane.add(scrol_tmp, BorderLayout.CENTER); tree_pane.setPreferredSize(TREE_SIZE); Color colour_two = Configuration.getColor("coloring.collection_tree_background", false); options_pane.setBackground(colour_two); options_pane.setBorder(BorderFactory.createEtchedBorder()); JScrollPane options_scroll_pane = new JScrollPane(options_pane); options_scroll_pane.setComponentOrientation(Dictionary.getOrientation()); JSplitPane mode_pane = new JSplitPane(); mode_pane.setComponentOrientation(Dictionary.getOrientation()); mode_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); if (Dictionary.getOrientation().isLeftToRight()){ mode_pane.add(tree_pane,JSplitPane.LEFT); mode_pane.add(options_scroll_pane,JSplitPane.RIGHT); mode_pane.setDividerLocation(TREE_SIZE.width); }else{ mode_pane.add(tree_pane,JSplitPane.RIGHT); mode_pane.add(options_scroll_pane,JSplitPane.LEFT); mode_pane.setDividerLocation(1-TREE_SIZE.width); } JPanel edit_pane = new JPanel(); edit_pane.setComponentOrientation(Dictionary.getOrientation()); edit_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2,0,0,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Download Setting"), BorderFactory.createEmptyBorder(2,2,2,2)))); edit_pane.setLayout(new BorderLayout()); edit_pane.add(mode_pane,BorderLayout.CENTER); edit_pane.add(button_pane,BorderLayout.PAGE_END); // Add to "this" setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); setLayout(new GridLayout(2,1)); add(edit_pane); add(list_scroll); mode = "Web"; generateOptions(options_pane,(Download)download_map.get(mode)); previous_path = tree.getSelectionPath(); } /** System Utilities */ public void modeChanged(int gli_mode) { // do nothing at this stage - should we be renewing download options?? } private void addHeader(String name, Color color, JPanel target_pane) { JPanel header = new JPanel(); header.setComponentOrientation(Dictionary.getOrientation()); header.setBackground(color); JPanel inner_pane = new JPanel(); inner_pane.setComponentOrientation(Dictionary.getOrientation()); inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createRaisedBevelBorder())); inner_pane.setBackground(color); JLabel header_label = new JLabel("" + name + ""); header_label.setComponentOrientation(Dictionary.getOrientation()); header_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false)); header_label.setHorizontalAlignment(JLabel.CENTER); header_label.setOpaque(true); // Layout inner_pane.setLayout(new BorderLayout()); inner_pane.add(header_label, BorderLayout.CENTER); header.setLayout(new BorderLayout()); header.add(inner_pane, BorderLayout.CENTER); target_pane.add(header); } /** Supporting Functions */ private Download loadDownload(String download_name, String lang) { Document document = null; InputStream input_stream = null; try { if (Gatherer.isGsdlRemote) { StringBuffer launch_str = new StringBuffer(); launch_str.append(Gatherer.cgiBase); launch_str.append("launch"); launch_str.append("?cmd=downloadinfo.pl&xml=&language="); launch_str.append(lang); launch_str.append("&plug="); launch_str.append(download_name); String launch = launch_str.toString(); System.err.println("*** launch = " + launch); URL launch_url = new URL(launch); URLConnection launch_connection = launch_url.openConnection(); input_stream = launch_connection.getInputStream(); } else { ArrayList args_list = new ArrayList(); String args[] = null; if(Utility.isWindows()) { if(Configuration.perl_path != null) { args_list.add(Configuration.perl_path); } else { args_list.add("Perl.exe"); } } args_list.add(LocalGreenstone.getBinScriptDirectoryPath()+"downloadinfo.pl"); args_list.add("-xml"); args_list.add("-language"); args_list.add(lang); args_list.add(download_name); // Create the process. args = (String []) args_list.toArray(new String[0]); Runtime runtime = Runtime.getRuntime(); DebugStream.println("Getting Download Info: "+args_list); Process process = runtime.exec(args); input_stream = process.getErrorStream(); } document = XMLTools.parseXML(input_stream); } catch (Exception error) { System.err.println("Failed when trying to parse: " + download_name); error.printStackTrace(); } if(document != null) { return parseXML(document.getDocumentElement()); } return null; } private Download parseXML(Node root) { Download download = new Download(); String node_name = null; for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) { node_name = node.getNodeName(); if(node_name.equalsIgnoreCase("Name")) { String name = XMLTools.getValue(node); download.setName(name); } else if (node_name.equalsIgnoreCase("Desc")) { download.setDescription(XMLTools.getValue(node)); } else if (node_name.equalsIgnoreCase("Abstract")) { download.setIsAbstract(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR)); } else if(node_name.equalsIgnoreCase("Arguments")) { for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) { node_name = arg.getNodeName(); if(node_name.equalsIgnoreCase("Option")) { Argument argument = new Argument((Element)arg); argument.parseXML((Element)arg); argument.setValue(argument.getDefaultValue()); download.addArgument(argument); } } } else if(node_name.equalsIgnoreCase("DownloadInfo")) { Download super_download = parseXML(node); download.setSuper(super_download); } } if(download.getName() != null) { return download; } return null; } /** Update the previous setup */ private boolean updateArguments(boolean checkRequired) { boolean cont = true; for(int i = 0; i < options_pane.getComponentCount(); i++) { Component component = options_pane.getComponent(i); if(component instanceof ArgumentControl) { cont = cont && ((ArgumentControl)component).updateArgument(checkRequired); } } if(cont){return true; } return false; } /** Generate Controls for Options */ /* at some stage we should think about which options should be shown for * different modes. Currently, always show all options (unless hidden)*/ private void generateOptions(JPanel options_pane, ArgumentContainer data) { options_pane.removeAll(); /** Create the current option panel */ ArrayList arguments = data.getArguments(true, false); int mode = Configuration.getMode(); ArrayList added_arguments = new ArrayList(); for(int i = 0; i < arguments.size(); i++) { Argument argument = (Argument) arguments.get(i); if (argument.isHiddenGLI()) continue; ArgumentControl argument_control = new ArgumentControl(argument,false,null); added_arguments.add(argument_control); } options_pane.setLayout(new GridLayout(added_arguments.size(),1)); for(int i = 0; i < added_arguments.size(); i++) { options_pane.add((ArgumentControl)added_arguments.get(i)); } } /** Behaviour Functions */ public void afterDisplay() { ready = true; } public void gainFocus() { if(!ready) { return; } // It is also a good time to determine if the download should be enabled - ie if its allowed to be enabled and a valid URL is present in the field. download_button_enabled = true; //download_button.setEnabled(download_button_enabled); } public void refresh(int refresh_reason, boolean ready) { } /** Private classes */ /** This tree provides a 'table of contents' for the various components of the design process (collection configuration in more technical terms). */ private class DesignTree extends JTree { private DesignNode root = null; /** Constructor. Automatically generates all of the nodes, in the order of CONTENTS. */ public DesignTree() { super(); resetModel(Configuration.getMode()); expandRow(0); setRootVisible(false); setSelectionRow(0); } /** Reset the model used by the design page contents tree. This is necessary to hide the partitions entry when in lower detail modes * @param mode the current detail mode as an int */ public void resetModel(int mode) { root = new DesignNode("DOWNLOAD.MODE.Root"); // Now add the design categories. for(int i = 0; i < CONTENTS.length; i++) { root.add(new DesignNode(CONTENTS[i])); } this.setModel(new DefaultTreeModel(root)); updateUI(); } /** Set the current view to the one specified. * @param type the name of the desired view as a String */ public void setSelectedView(String type) { type = Dictionary.get(type); for(int i = 0; i < root.getChildCount(); i++) { DesignNode child = (DesignNode) root.getChildAt(i); if(child.toString().equals(type)) { TreePath path = new TreePath(child.getPath()); setSelectionPath(path); } } } } /** A tree node that retains a reference to one of the possible design sub-views relating to the different sub-managers. */ private class DesignNode extends DefaultMutableTreeNode { /** Constructor. * @param object The Object assigned to this node. */ public DesignNode(String object) { super(object); } /** Retrieve a textual representation of the object. * @return a String */ public String toString() { // return Dictionary.get("CDM.GUI." + (String)getUserObject()); return Dictionary.get((String) getUserObject()); } } /** Listens for selection changes in the 'contents' tree, and switches to the appropriate view. */ private class TreeListener implements TreeSelectionListener { /** Called whenever the selection changes, we must update the view so it matches the node selected. * @param event A TreeSelectionEvent containing more information about the tree selection. * @see org.greenstone.gatherer.cdm.ClassifierManager * @see org.greenstone.gatherer.cdm.CollectionDesignManager * @see org.greenstone.gatherer.cdm.CollectionMetaManager * @see org.greenstone.gatherer.cdm.FormatManager * @see org.greenstone.gatherer.cdm.LanguageManager * @see org.greenstone.gatherer.cdm.MetadataSetView * @see org.greenstone.gatherer.cdm.SubcollectionManager * @see org.greenstone.gatherer.cdm.TranslationView * @see org.greenstone.gatherer.cdm.PlugInManager */ public void valueChanged(TreeSelectionEvent event) { if(!tree.isSelectionEmpty()) { TreePath path = tree.getSelectionPath(); DesignNode node = (DesignNode)path.getLastPathComponent(); String type = (String)node.getUserObject(); Gatherer.g_man.wait(true); if(type == CONTENTS[0]) { mode = "Web"; generateOptions(options_pane,(Download)download_map.get(mode)); } else if(type == CONTENTS[1]) { mode = "MediaWiki"; generateOptions(options_pane,(Download)download_map.get(mode)); } else if(type == CONTENTS[2]) { mode = "OAI"; generateOptions(options_pane,(Download)download_map.get(mode)); } else if(type == CONTENTS[3]) { mode = "Z3950"; generateOptions(options_pane,(Download)download_map.get(mode)); } else if(type == CONTENTS[4]) { mode = "SRW"; generateOptions(options_pane,(Download)download_map.get(mode)); } tree.setSelectionPath(path); previous_path = path; repaint(); Gatherer.g_man.wait(false); } } } private class ClearCacheListener implements ActionListener { public void actionPerformed(ActionEvent event) { // Retrieve the cache folder and delete it. Utility.delete(Utility.getCacheDir()); // ...and refresh the node in the workspace tree to show it's all gone Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED); } } private class DownloadButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { if(checkURL(true) && checkProxy() == true) { // Proxy settings are now set. Check that the url is not a redirect, else get // redirect url (we do this step in order to avoid some unintuitive behaviour from wget) Download current_download = (Download)download_map.get(mode); Argument arg_url = current_download.getArgument("url"); if(arg_url != null) { // it's null for z3950 and possibly for other downloaders String url_str = arg_url.getValue(); String redirect_url_str = getRedirectURL(url_str); // only update the Argument and its GUI ArgumentControl if the URL // has in fact changed if(!url_str.equals(redirect_url_str)) { arg_url.setValue(redirect_url_str); updateArgument(arg_url, redirect_url_str); } } getter.newDownloadJob((Download)download_map.get(mode) ,mode,proxy_url); } } } /** * The Java code here will retrieve the page at the given url. If the response code is * a redirect, it will get the redirect url so that wget may be called with the proper url. * This preprocessing of the URL is necessary because: * Wget does not behave the way the browser does when faced with urls of the form * http://www.englishhistory.net/tudor/citizens and if that page does not exist. * The directory listing with a slash at the end (http://www.englishhistory.net/tudor/citizens/) * does exist, however. In order to prevent wget from assuming that the root URL * to traverse is http://www.englishhistory.net/tudor/ instead of the intended * http://www.englishhistory.net/tudor/citizens/, we need give wget the redirect location * that's returned when we initially make a request for http://www.englishhistory.net/tudor/citizens * The proper url is sent back in the Location header, allowing us to bypass wget's * unexpected behaviour. * This method ensures that urls like http://www.nzdl.org/niupepa also continue to work: * there is no http://www.nzdl.org/niupepa/ page, because this url actually redirects to an * entirely different URL. * @return the redirect url for the given url if any redirection is involved, or the * url_str. */ private String getRedirectURL(String url_str) { HttpURLConnection connection = null; if(url_str.startsWith("http:")) { // only test http urls try { URL url = new URL(url_str); connection = (HttpURLConnection)url.openConnection(); //new HttpURLConnection(url); // don't let it automatically follow redirects, since we want to // find out whether we are dealing with redirects in the first place connection.setInstanceFollowRedirects(false); // now check for whether we get a redirect response // HTTP Codes 3xx are redirects, http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html int responseCode = connection.getResponseCode(); if(responseCode >= 300 && responseCode < 400) { String responseMsg = connection.getResponseMessage(); // Get the Location header since this specifies the new location of the resource String location = connection.getHeaderField("Location"); // this becomes the url that wget should download from url_str = location.trim(); } connection.disconnect(); } catch(Exception e) { if(connection != null) { connection.disconnect(); } System.err.println("Checking redirection. Tried to connect to " + url_str + ",\nbut got exception: " + e); } } return url_str; } /** For a string-based Argument whose value has changed, this method * updates the GUI ArgumentControl's value correspondingly. */ private void updateArgument(Argument arg, String value) { for(int i = 0; i < options_pane.getComponentCount(); i++) { Component component = options_pane.getComponent(i); if(component instanceof ArgumentControl) { ArgumentControl control = (ArgumentControl)component; if(control.getArgument() == arg) { control.setValue(value); control.repaint(); } } } } private boolean checkURL(boolean checkRequired){ if (!updateArguments(checkRequired)){ return false; } Download current_download = (Download)download_map.get(mode); Argument arg_url = current_download.getArgument("url"); if (arg_url == null) return true; String url_str = arg_url.getValue(); URL url = null; try { url = new URL(url_str); } catch(MalformedURLException error) { JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Mirroring.Invalid_URL"), Dictionary.get("Mirroring.Invalid_URL_Title"), JOptionPane.ERROR_MESSAGE); return false; } return true; } private boolean checkProxy(){ proxy_url = null; Download current_download = (Download)download_map.get(mode); Argument arg = current_download.getArgument("proxy_on"); if (arg == null) return true; // Determine if we have to use a proxy. if(Configuration.get("general.use_proxy", true)) { String proxy_host = Configuration.getString("general.proxy_host", true); String proxy_port = Configuration.getString("general.proxy_port", true); // Find out whether the user has already authenticated themselves String user_pass = ""; String address = proxy_host + ":" + proxy_port; int count = 0; // Only for wget, need to avoid a second automatic authentication popup (first asks // the proxy authentication for wget, and the second will ask the same for the realm) // Once the authentication has been reused, it will set the GAuthenticator state back to REGULAR GAuthenticator.setMode(GAuthenticator.DOWNLOAD); while(count < 3 && (user_pass = (String) GAuthenticator.authentications.get(address)) == null) { Authenticator.requestPasswordAuthentication(proxy_host, null, Integer.parseInt(proxy_port), "http://", Dictionary.get("WGet.Prompt"), "HTTP"); count++; } if(count >= 3) { return false; } if(user_pass.indexOf("@") != -1) { // Write the use proxy command - we don't do this anymore, instead we set environment variables - hopefully these can't be spied on like the following can (using ps) - actually the environment stuff didn't work for windows, so lets go back to this if (Utility.isWindows()) { arg.setValue("true"); arg.setAssigned(true); arg = current_download.getArgument("proxy_host"); arg.setValue(proxy_host); arg.setAssigned(true); arg = current_download.getArgument("proxy_port"); arg.setValue(proxy_port); arg.setAssigned(true); arg = current_download.getArgument("user_name"); arg.setValue(user_pass.substring(0, user_pass.indexOf("@"))); arg.setAssigned(true); arg = current_download.getArgument("user_password"); arg.setValue(user_pass.substring(user_pass.indexOf("@") + 1)); arg.setAssigned(true); } else{ String user_name = user_pass.substring(0, user_pass.indexOf("@")); String user_pwd = user_pass.substring(user_pass.indexOf("@") + 1); proxy_url = user_name+":"+user_pwd+"@"+proxy_host+":"+proxy_port+"/"; } return true; } else{ return false; } } return true; } /* private class PreferencesButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { new Preferences(Preferences.CONNECTION_PREFS); } }*/ private class InformationButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { //turn off the check for find argument Download current_download = (Download)download_map.get(mode); if (!checkProxy() || !checkURL(false) )return; if(server_info != null) { server_info.dispose(); } Argument arg_url = current_download.getArgument("url"); String str_url = ""; if( arg_url!= null && arg_url.isAssigned()) { str_url = arg_url.getValue(); } server_info = new ServerInfoDialog(str_url ,proxy_url, mode,(Download)download_map.get(mode)); } } private class PreferencesButtonActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { new Preferences(Preferences.CONNECTION_PREFS); } } }