source: main/trunk/gli/src/org/greenstone/gatherer/collection/CollectionManager.java@ 22410

Last change on this file since 22410 was 22410, checked in by ak19, 14 years ago

Getting collectgroup to work for a remote Greenstone server. Need to update gliserver.pl still to work with the collectgroup changes. 1. GLI side changes: no longer does the collectdir parameter to import and build include the colgroup at the end, it is now back to being everything upto the GS collect dir. Instead, the loaded collection is colgroup/subcolname. 2. Changed CollectionManager.getLoadedCollectionName() to return (colgroup/)subcolname instead of the subcolname excluding groupname. 3. This required changes in other classes, including replacing unnecessary explicit calls to getLoadedGroupQualifiedCollectionName(). 4. Added another variant: getLoadedCollectionName(boolean). If the parameter is true, it will put a url style slash between colgroup and subcolname, otherwise use the OS dependent FileSeparator. 5. Changes to RemoteGreenstoneServer to pass the group/subcol as the collectionname to gliserver.pl, with a vertical bar separator. 6. Minor changes like: WorkspaceTreeNode now knows not to load a collection group if it's already in the loaded collection (previously handed only sub collection name), removed old debugging statements and unused import statements.

  • Property svn:keywords set to Author Date Id Revision
File size: 86.3 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.collection;
38
39import java.io.*;
40import java.util.*;
41import javax.swing.*;
42import javax.swing.event.*;
43import javax.swing.filechooser.FileSystemView;
44import javax.swing.tree.*;
45import org.greenstone.gatherer.Configuration;
46import org.greenstone.gatherer.DebugStream;
47import org.greenstone.gatherer.Dictionary;
48import org.greenstone.gatherer.Gatherer;
49import org.greenstone.gatherer.cdm.CollectionDesignManager;
50import org.greenstone.gatherer.cdm.CollectionMeta;
51import org.greenstone.gatherer.cdm.CollectionMetaManager;
52import org.greenstone.gatherer.cdm.CommandTokenizer;
53import org.greenstone.gatherer.cdm.BuildTypeManager;
54import org.greenstone.gatherer.cdm.CollectionConfiguration;
55import org.greenstone.gatherer.greenstone.Classifiers;
56import org.greenstone.gatherer.greenstone.LocalGreenstone;
57import org.greenstone.gatherer.greenstone.LocalLibraryServer;
58import org.greenstone.gatherer.greenstone.Plugins;
59import org.greenstone.gatherer.greenstone3.ServletConfiguration;
60import org.greenstone.gatherer.gui.LockFileDialog;
61import org.greenstone.gatherer.gui.ModalProgressPopup;
62import org.greenstone.gatherer.gui.WarningDialog;
63import org.greenstone.gatherer.metadata.DocXMLFileManager;
64import org.greenstone.gatherer.metadata.MetadataChangedListener;
65import org.greenstone.gatherer.metadata.MetadataSet;
66import org.greenstone.gatherer.metadata.MetadataSetManager;
67import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
68import org.greenstone.gatherer.metadata.ProfileXMLFileManager;
69import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
70import org.greenstone.gatherer.shell.GShell;
71import org.greenstone.gatherer.shell.GShellEvent;
72import org.greenstone.gatherer.shell.GShellListener;
73import org.greenstone.gatherer.shell.GShellProgressMonitor;
74import org.greenstone.gatherer.util.Codec;
75import org.greenstone.gatherer.util.StaticStrings;
76import org.greenstone.gatherer.util.Utility;
77import org.greenstone.gatherer.util.XMLTools;
78import org.w3c.dom.*;
79
80/** This class manages many aspects of the collection, from its creation via scripts, data access via methods and its importing and building into the final collection. It is also responsible for firing appropriate event when significant changes have occured within the collection, and for creating a new metadata set manager as necessary.
81 * @author John Thompson
82 * @version 2.3
83 */
84public class CollectionManager
85 implements GShellListener, MetadataChangedListener
86{
87 /** Are we currently in the process of building? */
88 static private boolean building = false;
89 /** Are we currently in the process of importing? */
90 static private boolean importing = false;
91 /** Are we currently in the process of scheduling? */
92 static private boolean scheduling = false;
93 /** The objects listening for CollectionContentsChanged events. */
94 static private ArrayList collection_contents_changed_listeners = new ArrayList();
95 /** The collection this manager is managing! */
96 static private Collection collection = null;
97 /** The collection tree (used in both Gather and Enrich panes). */
98 static private CollectionTree collection_tree = null;
99 /** The collection tree model. */
100 static private CollectionTreeModel collection_tree_model = null;
101 /** An inner class listener responsible for noting tree changes and resetting saved when they occur. */
102 static private FMTreeModelListener fm_tree_model_listener = null;
103 /** The monitor responsible for parsing the build process. */
104 static private GShellProgressMonitor build_monitor = null;
105 /** The monitor responsible for parsing the import process. */
106 static private GShellProgressMonitor import_monitor = null;
107 /** The monitor responsible for parsing the scheduler process. */
108 static private GShellProgressMonitor schedule_monitor = null;
109
110 /** The name of the standard lock file. */
111 static final public String LOCK_FILE = "gli.lck";
112
113 /** Used to indicate the source of the message is the file collection methods. */
114 static final public int COLLECT = 3;
115 /** Used to indicate the source of the message is the building methods. */
116 static final public int BUILDING = 5;
117 /** Used to indicate the source of the message is in the scheduling methods...? */
118 static final public int SCHEDULING = 7;
119
120 /** To store the path to the perl scripts. In the case of local Greenstone servers,
121 * this will be the local bin/script folder. */
122 static private String scriptPath = "";
123
124 /** Constructor. */
125 public CollectionManager() {
126 // Initialisation.
127 this.building = false;
128 this.importing = false;
129 this.scheduling = false;
130 this.collection = null;
131
132 MetadataXMLFileManager.addMetadataChangedListener(this);
133
134 // If using a remote Greenstone server, delete the local collect directory because it will be out of date
135 if (Gatherer.isGsdlRemote) {
136 System.err.println("Deleting user's local collect directory...");
137 Utility.delete(new File(Gatherer.getCollectDirectoryPath()));
138 System.err.println("Done.");
139 new File(Gatherer.getCollectDirectoryPath()).mkdirs();
140
141 scriptPath = ""; // remote greenstone: scriptPath will be determined on remote server side
142 }
143 else { // local greenstone case: scripts are inside bin/script
144 scriptPath = LocalGreenstone.getBinScriptDirectoryPath();
145 }
146 }
147
148
149 static public void addCollectionContentsChangedListener(CollectionContentsChangedListener listener)
150 {
151 collection_contents_changed_listeners.add(listener);
152 }
153
154
155 /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
156 * @see org.greenstone.gatherer.Configuration
157 * @see org.greenstone.gatherer.Gatherer
158 * @see org.greenstone.gatherer.collection.Collection
159 * @see org.greenstone.gatherer.gui.BuildOptions
160 * @see org.greenstone.gatherer.shell.GShell
161 * @see org.greenstone.gatherer.shell.GShellListener
162 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
163 * @see org.greenstone.gatherer.util.Utility
164 */
165 public void buildCollection()
166 {
167
168 DebugStream.println("In CollectionManager.buildCollection(), CollectionDesignManager.isCompleteBuild(): " + CollectionDesignManager.isCompleteBuild());
169 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
170 building = true;
171
172 // Generate the buildcol.pl command
173 ArrayList command_parts_list = new ArrayList();
174 if (!Gatherer.isGsdlRemote) {
175 command_parts_list.add(Configuration.perl_path);
176 command_parts_list.add("-S");
177 }
178
179 if (Configuration.fedora_info.isActive()) {
180 command_parts_list.add(scriptPath + "g2f-buildcol.pl");
181
182 command_parts_list.add("-hostname");
183 command_parts_list.add(Configuration.fedora_info.getHostname());
184
185 command_parts_list.add("-port");
186 command_parts_list.add(Configuration.fedora_info.getPort());
187
188 command_parts_list.add("-username");
189 command_parts_list.add(Configuration.fedora_info.getUsername());
190
191 command_parts_list.add("-password");
192 command_parts_list.add(Configuration.fedora_info.getPassword());
193
194 command_parts_list.add("-protocol");
195 command_parts_list.add(Configuration.fedora_info.getProtocol());
196
197 }
198 else {
199
200 if ( !CollectionDesignManager.isCompleteBuild() && CollectionDesignManager.index_manager.isLucene() ) {
201 command_parts_list.add(scriptPath + "incremental-buildcol.pl");
202 CollectionDesignManager.setBuildcolWasFull(false);
203 } else {
204 command_parts_list.add(scriptPath + "full-buildcol.pl");
205 CollectionDesignManager.setBuildcolWasFull(true);
206 }
207 }
208
209 command_parts_list.add("-gli");
210 command_parts_list.add("-language");
211 command_parts_list.add(Configuration.getLanguage());
212 if(!Gatherer.isGsdlRemote) {
213 command_parts_list.add("-collectdir");
214 command_parts_list.add(getCollectDirectory()); // <../collect/>
215 }
216
217 String[] build_options = collection.build_options.getValues();
218 for (int i = 0; i < build_options.length; i++) {
219 command_parts_list.add(build_options[i]);
220 }
221
222 command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
223
224 // Run the buildcol.pl and
225 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
226 GShell shell = new GShell(command_parts, GShell.BUILD, BUILDING, this, build_monitor, GShell.GSHELL_BUILD);
227 shell.addGShellListener(Gatherer.g_man.create_pane);
228 shell.addGShellListener(Gatherer.g_man.format_pane);
229 shell.start();
230
231 }
232
233 /*probably repeating alot of work, but I want to keep this separate... wendy*/
234 public void scheduleBuild()
235 {
236 DebugStream.println("In CollectionManager.scheduleBuild(), CollectionDesignManager.isCompleteBuild(): " + CollectionDesignManager.isCompleteBuild());
237 DebugStream.println("Is event dispatch threa: " + SwingUtilities.isEventDispatchThread());
238
239 ArrayList sched_list = new ArrayList();
240 if (!Gatherer.isGsdlRemote) {
241 sched_list.add(Configuration.perl_path);
242 sched_list.add("-S");
243 }
244 sched_list.add(scriptPath + "schedule.pl");
245 sched_list.add("-colname");
246 sched_list.add(collection.getName());
247 sched_list.add("-gli");
248
249 // First, generate the import.pl command, also converting to a string
250 // Generate the import.pl command
251 ArrayList import_list = new ArrayList();
252 if (!Gatherer.isGsdlRemote) {
253 import_list.add(Configuration.perl_path);
254 import_list.add("-S");
255 }
256
257 String cmdPrefix = CollectionDesignManager.isCompleteBuild() ? "full-" : "incremental-";
258 import_list.add(scriptPath + cmdPrefix + "import.pl");
259 import_list.add("-language");
260 import_list.add(Configuration.getLanguage());
261 if(!Gatherer.isGsdlRemote) {
262 import_list.add("-collectdir");
263 import_list.add(getCollectDirectory());
264 }
265
266 String[] import_options = collection.import_options.getValues();
267 int i = 0;
268 for (i = 0; i < import_options.length; i++) {
269 import_list.add(import_options[i]);
270 }
271
272 import_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
273
274 String[] import_parts = (String[]) import_list.toArray(new String[0]);
275 String command = "";
276 i = 0;
277 for (i = 0; i < import_parts.length-1; i++) {
278 command = command + import_parts[i] + " ";
279 }
280 command = command + import_parts[i];
281
282 sched_list.add("-import");
283 sched_list.add("\"" + command + "\"");
284
285 // Generate the buildcol.pl command, also converting to a string
286 ArrayList build_list = new ArrayList();
287
288 // i'm not doing this in schedule.pl right now - should i be?
289 if (!Gatherer.isGsdlRemote) {
290 build_list.add(Configuration.perl_path);
291 build_list.add("-S");
292 }
293
294 String buildType = (new CollectionMeta( CollectionDesignManager.collect_config.getBuildType() )).getValue(CollectionMeta.TEXT);
295 if ( !CollectionDesignManager.isCompleteBuild() && buildType.equals( BuildTypeManager.BUILD_TYPE_LUCENE ) ) {
296 build_list.add(scriptPath + "incremental-buildcol.pl");
297 } else {
298 build_list.add(scriptPath + "full-buildcol.pl");
299 }
300
301 build_list.add("-language");
302 build_list.add(Configuration.getLanguage());
303 if(!Gatherer.isGsdlRemote) {
304 build_list.add("-collectdir");
305 build_list.add(getCollectDirectory());
306 }
307
308 String[] build_options = collection.build_options.getValues();
309 for (i = 0; i < build_options.length; i++) {
310 build_list.add(build_options[i]);
311 }
312
313 build_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
314
315 //build actual string
316 String[] build_parts = (String[]) build_list.toArray(new String[0]);
317 String command2 = "";
318 for(i = 0; i < build_parts.length-1; i++) {
319 command2 = command2 + build_parts[i] + " ";
320 }
321 command2 = command2 + build_parts[i];
322
323 sched_list.add("-build");
324 sched_list.add("\"" + command2 + "\"");
325
326 //next, the scheduling frequency goes here
327 String[] schedule_options = collection.schedule_options.getValues();
328 for(i = 0; i < schedule_options.length; i++) {
329 sched_list.add(schedule_options[i]);
330 }
331
332 //now, hope it will run. ;)
333 String[] sched_parts = (String[]) sched_list.toArray(new String[0]);
334
335 GShell shell = new GShell(sched_parts, GShell.SCHEDULE, SCHEDULING, this, schedule_monitor, GShell.GSHELL_SCHEDULE);
336 shell.addGShellListener(Gatherer.g_man.create_pane);
337 shell.addGShellListener(Gatherer.g_man.format_pane);
338 shell.start();
339 }
340
341 /** Used to determine whether the currently active collection has been built.
342 * @return A boolean indicating the built status of the collection.
343 */
344 public boolean built() {
345 if(collection != null) {
346 // Determine if the collection has been built by looking for the build.cfg (gs2)
347 // buildConfig.xml (gs3) or export.inf (fedora) file
348 String file_name = "";
349
350 if (Configuration.fedora_info != null && Configuration.fedora_info.isActive()) { // FLI case
351 // Fedora build
352 //file_name = getLoadedCollectionArchivesDirectoryPath() + "import.inf";
353 //file_name = getLoadedCollectionExportDirectoryPath() + "export.inf"; // export.pl no longer generates this
354 file_name = getLoadedCollectionExportDirectoryPath() + "archiveinf-doc.gdb";
355 } else {
356 // GLI is running, check if it's greenstone 3 or greenstone 2
357 if (Gatherer.GS3) { // GS3 GLI
358 file_name = getLoadedCollectionIndexDirectoryPath() + Utility.BUILD_CONFIG_XML;
359 }
360 else { // greenstone 2 GLI
361 file_name = getLoadedCollectionIndexDirectoryPath() + Utility.BUILD_CFG;
362 }
363 }
364 File test_file = new File(file_name);
365 return test_file.exists();
366 }
367 return false;
368 }
369
370 /** Used to determine whether the currently active collection has been imported.
371 * @return A boolean indicating the imported status of the collection.
372 */
373 public boolean imported() {
374 if ( collection != null ) {
375 String file_name = getLoadedCollectionDirectoryPath() + "archives";
376 File test_file = new File(file_name);
377 return test_file.exists();
378 }
379 return false;
380 }
381
382 /** a test method to see if we can delete a directory/file - returns false is the file or any of the contents of a directory cannot be deleted */
383 static private boolean canDelete(File file)
384 {
385 if (!file.isDirectory()) {
386 return file.canWrite();
387 }
388 File [] file_list = file.listFiles();
389 for (int i=0; i<file_list.length; i++) {
390 if (!canDelete(file_list[i])) {
391 return false;
392 }
393 }
394 return true;
395 }
396
397
398 /** Called to close the current collection and remove its lock file.
399 * @see org.greenstone.gatherer.Gatherer
400 * @see org.greenstone.gatherer.collection.Collection
401 * @see org.greenstone.gatherer.util.Utility
402 */
403 public void closeCollection() {
404 DebugStream.println("Close collection: " + collection.getName());
405
406 // Remove the lock on this file, then remove the collection.
407 File lock_file = new File(getLoadedCollectionDirectoryPath() + LOCK_FILE);
408 lock_file.delete();
409 if (lock_file.exists()) {
410 System.err.println("Warning: Lockfile was not successfully deleted.");
411 }
412
413 // Remove the lock file on the server
414 if (Gatherer.isGsdlRemote) {
415 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), lock_file);
416 }
417
418 MetadataSetManager.clearMetadataSets();
419 MetadataXMLFileManager.clearMetadataXMLFiles();
420 DocXMLFileManager.clearDocXMLFiles();
421 ProfileXMLFileManager.clearProfileXMLFile();
422
423 collection.destroy();
424 collection = null;
425 collection_tree_model = null;
426 //Configuration.setCollectionConfiguration(null);
427 Gatherer.refresh(Gatherer.COLLECTION_CLOSED);
428 if (Gatherer.g_man != null) {
429 Gatherer.g_man.updateUI(); // !!! Necessary?
430 }
431 }
432
433//This method is no longer used in gs3 since the modification of CollectionConfiguration.java
434// public void convertToGS3Collection() {
435// // Generate the convert_coll_from_gs2.pl command
436// ArrayList command_parts_list = new ArrayList();
437// if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
438// command_parts_list.add(Configuration.perl_path);
439// command_parts_list.add("-S");
440// }
441// command_parts_list.add(Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl");
442// command_parts_list.add("-collectdir");
443// command_parts_list.add(getCollectDirectory());
444// command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
445//
446// // Run the convert_coll_from_gs2.pl command
447// String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
448// GShell process = new GShell(command_parts, GShell.CONVERT, COLLECT, this, null, GShell.GSHELL_CONVERT);
449// process.addGShellListener(this);
450// process.run(); // Don't bother threading this... yet
451//
452// }
453
454 /** When basing a new collection on an existing one, we need to copy
455 * over some extra directories: all except import, archives, building, index
456 * really we just want images, macros, perllib, but there may also be eg style, or other dirs.
457 */
458 private boolean copyExtraBaseCollStuff(File new_coll_dir, File base_coll_dir) {
459 if (!new_coll_dir.isDirectory() || !base_coll_dir.isDirectory()) {
460 return false;
461 }
462 DebugStream.println("Copying extra dirs from the base collection");
463
464
465 File subdirs[] = base_coll_dir.listFiles();
466 for (int i = 0; subdirs != null && i < subdirs.length; i++) {
467 File subdir = subdirs[i];
468 if (subdir.isDirectory()) {
469 String dir_name = subdir.getName();
470 // ignore those we don't need, (archives, buildng, index) and
471 // those we are handling in another place (import, etc, metadata)
472 if (dir_name.startsWith ("import") || dir_name.startsWith("archives") || dir_name.startsWith("building") || dir_name.startsWith("index") || dir_name.startsWith("etc") || dir_name.startsWith("metadata") || dir_name.startsWith("log") || dir_name.startsWith("tmp")) {
473 continue;
474 }
475 try {
476 // copy the directory
477 File new_coll_subdir = new File(new_coll_dir, dir_name);
478 new_coll_subdir.mkdirs();
479 Gatherer.f_man.getQueue().copyDirectoryContents(subdir, new_coll_subdir);
480 }
481 catch (Exception e) {
482 DebugStream.println("Couldn't copy over the" + subdir+" dir from the base collection: "+e.toString());
483 }
484 }
485 }
486
487 return true;
488 }
489
490 /** Used to set the current collection to the given collection. Note that this call should -always- be proceeded by a ready call, and if the collection is ready and the saved flag is unset then the user should be prompted to save. Also note that this method creates yet another GShell to run buildcol.pl.
491 * @param description a description of the collection as a String
492 * @param email the email address of the author/maintainer as a String
493 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
494 * @param title the longer title of the collection as a String
495 * @param base_collection_directory if the user has chosen to base their new collection on an existing one, this is the directory where this base collection can be found, as a File, otherwise its null
496 * @param metadata_sets if the user has decided to select several metadata sets with which to initially populate the GLI then this is an ArrayList of metadata set file names, otherwise its null
497 */
498 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
499 {
500 // Display a modal progress popup to indicate that the collection is being loaded
501 ModalProgressPopup create_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Creating_Collection"), Dictionary.get("CollectionManager.Creating_Collection_Please_Wait"));
502 create_collection_progress_popup.display();
503
504 // Create the collection on a separate thread so the progress bar updates correctly
505 (new CreateCollectionTask(description, email, name, title, base_collection_directory, metadata_sets, create_collection_progress_popup)).start();
506 }
507
508
509 private class CreateCollectionTask
510 extends Thread
511 {
512 private String description = null;
513 private String email = null;
514 private String name = null;
515 private String title = null;
516 private File base_collection_directory = null;
517 private ArrayList metadata_sets = null;
518 private ModalProgressPopup create_collection_progress_popup = null;
519
520 public CreateCollectionTask(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets, ModalProgressPopup create_collection_progress_popup)
521 {
522 this.description = description;
523 this.email = email;
524 this.name = name;
525 this.title = title;
526 this.base_collection_directory = base_collection_directory;
527 this.metadata_sets = metadata_sets;
528 this.create_collection_progress_popup = create_collection_progress_popup;
529 }
530
531 public void run()
532 {
533 createCollectionInternal(description, email, name, title, base_collection_directory, metadata_sets);
534 create_collection_progress_popup.close();
535 }
536 }
537
538
539 private void createCollectionInternal(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
540 {
541 try {
542 // first make sure that the collect directory exists
543 File collect_dir = new File(getDefaultCollectDirectory());
544 if (!collect_dir.exists()) {
545 collect_dir.mkdirs();
546 }
547
548 // Create the new collection
549 makeCollection(name, email);
550
551 // Check that the collection has been created successfully
552 String collection_directory_path = getCollectionDirectoryPath(name);
553 if (!new File(collection_directory_path).exists()) {
554 // If there is no collection directory then the creation was unsuccessful, or cancelled
555
556 return;
557 }
558
559 // Check for the existence of the collection configuration file
560 String file_name = ((Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG);
561 File collect_cfg_file = new File(collection_directory_path + "etc" + File.separator + file_name);
562
563 if (!collect_cfg_file.exists()) {
564 System.err.println("Error: no " + file_name + " file has been created!");
565 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Create_Collection_With_Reason", Dictionary.get("CollectionManager.No_Config_File")), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
566 return;
567 }
568
569 // ACTIVE_DIR/log/
570 File log_dir = new File(collection_directory_path + "log");
571 log_dir.mkdirs();
572
573 // Make sure an import folder exists
574 File collection_import_directory = new File(collection_directory_path + "import");
575 if (!collection_import_directory.exists()) {
576 collection_import_directory.mkdirs();
577 if (Gatherer.isGsdlRemote) {
578 Gatherer.remoteGreenstoneServer.newCollectionDirectory(name, collection_import_directory);
579 }
580 }
581
582 // Now create the collection object around the directory.
583 collection = new Collection(new File(collection_directory_path, "gli.col"));
584
585 // for remote case, scheduling causes an Exception on creating a new collection that
586 // can't be recovered from. For GS3, it doesn't work since it it trying to access etc/main.cfg
587 if (canDoScheduling()) {
588 scheduling();
589 }
590
591 MetadataSetManager.clearMetadataSets();
592 MetadataXMLFileManager.clearMetadataXMLFiles();
593 DocXMLFileManager.clearDocXMLFiles();
594
595 // Import default metadata sets, if any
596 // for (int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
597 // importMetadataSet((MetadataSet) metadata_sets.get(i));
598 // }
599
600 ProfileXMLFileManager.loadProfileXMLFile(new File(collection_directory_path + "metadata"));
601
602 // Before creating the CollectionDesignManager check if we are basing it upon some other collection
603 if (base_collection_directory != null) {
604 DebugStream.println("Basing new collection on existing one: " + base_collection_directory);
605
606 // If we're using a remote Greenstone server, download the collection shell to get the files needed
607 if (Gatherer.isGsdlRemote) {
608 String base_collection_name = base_collection_directory.getName();
609 Gatherer.remoteGreenstoneServer.downloadCollection(base_collection_name);
610 }
611
612 collection.setBaseCollection(base_collection_directory.getAbsolutePath());
613 // copy over other needed directories
614 copyExtraBaseCollStuff(new File(collection_directory_path), base_collection_directory);
615
616 // Try to import any existing metadata sets for this collection
617 // Look in base_collection_directory/metadata and import any metadata sets found.
618 File base_metadata_directory = new File(base_collection_directory, "metadata");
619 ArrayList base_metadata_sets = MetadataSetManager.listMetadataSets(base_metadata_directory);
620 if (base_metadata_sets != null) {
621 for (int i = 0; i < base_metadata_sets.size(); i++) {
622 importMetadataSet((MetadataSet) base_metadata_sets.get(i));
623 }
624 }
625 else {
626 DebugStream.println("This base collection has no metadata directory.");
627 }
628
629 // Now we update our collect.cfg
630 DebugStream.println("Copy and update " + file_name + " from base collection.");
631
632 if (Gatherer.GS3 == true) {
633 updateCollectionConfigXML(new File(base_collection_directory, Utility.CONFIG_GS3_FILE),
634 new File(collection_directory_path, Utility.CONFIG_GS3_FILE));
635 } else {
636 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_FILE),
637 new File(collection_directory_path, Utility.CONFIG_FILE),
638 description, email, title);
639 }
640 }
641 else {
642 // only load metadata sets here if we have not based the collection on any other.
643 // Load the default metadata sets
644 addDefaultMetadataSets();
645
646 // Make sure we always have the extracted metadata set
647 addRequiredMetadataSets();
648 }
649
650 collection.cdm = new CollectionDesignManager(new File(getLoadedCollectionCfgFilePath()));
651
652 // We always set title and description here rather than calling mkcol.pl with Unicode arguments
653 CollectionMeta collection_name_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR);
654 collection_name_collectionmeta.setValue(title);
655 CollectionMeta collection_extra_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
656 collection_extra_collectionmeta.setValue(description);
657
658 // Now that we have a CDM, update several settings, such as if we created this collection by basing it on another, set it as public automatically. This update is done to the internal xml structure which may be saved into collect.cfg or collectionConfig.xml accordingly.
659 if (base_collection_directory != null) {
660 // Update the creator and maintainer
661 CollectionMeta creator_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getCreator());
662 creator_collectionmeta.setValue(email);
663 creator_collectionmeta = null;
664 CollectionMeta maintainer_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getMaintainer());
665 maintainer_collectionmeta.setValue(email);
666 maintainer_collectionmeta = null;
667
668 // All collections based on others are automatically public
669 CollectionMeta public_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getPublic());
670 public_collectionmeta.setValue(StaticStrings.TRUE_STR);
671 public_collectionmeta = null;
672
673 // Finally reset the icons
674 CollectionMeta icon_collection_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR);
675 icon_collection_collectionmeta.setValue(StaticStrings.EMPTY_STR);
676 icon_collection_collectionmeta = null;
677 CollectionMeta icon_collection_small_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
678 icon_collection_small_collectionmeta.setValue(StaticStrings.EMPTY_STR);
679 icon_collection_small_collectionmeta = null;
680 }
681
682 saveCollection();
683
684 // Create a lock file
685 createLockFile(new File(collection_directory_path, LOCK_FILE));
686
687 // We're done. Let everyone know.
688 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
689 }
690 catch (Exception error) {
691 DebugStream.printStackTrace(error);
692 }
693 }
694
695 private void scheduling()
696 throws Exception
697 {
698 //try to obtain email address of collection owner if it exists...
699 String stmp = Configuration.getEmail();
700 if(stmp != null) {
701 collection.schedule_options.setValue("toaddr", false, Configuration.getEmail());
702 }
703
704 //The next few items deal with updating the SMTP server, and the to: and from: addresses
705 //from main.cfg and the collection configuration. if no changes are made, or the
706 //values are result to NULL, any existing values are kept.
707
708 //try to obtain email address of Greenstone installation webmaster for - used to indicate "sender".
709 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
710 BufferedReader maincfg = new BufferedReader(new FileReader(mcfg));
711 stmp = "";
712 String fromaddr = "";
713 while((stmp = maincfg.readLine()) != null) {
714 if(stmp.startsWith("maintainer")) {
715 fromaddr = stmp.substring(10); //length of MailServer
716 fromaddr = fromaddr.trim();
717 break;
718 }
719 }
720 maincfg.close();
721 if(!fromaddr.equals("NULL") && !fromaddr.equals("null")) {
722 collection.schedule_options.setValue("fromaddr", false, fromaddr);
723 }
724
725 //try to obtain an smtp server address from main.cfg. If that fails,
726 //try mail.server if an email address exists. If that fails,
727 //maybe a message to set attribute in main.cfg?
728 //i'm pretty sure there exists functionality to do this, but
729 //i'll finish this faster if I just wrote it
730
731
732 maincfg = new BufferedReader(new FileReader(mcfg));
733 String smtptmp = "NULL";
734 while((stmp = maincfg.readLine()) != null) {
735 if(stmp.startsWith("MailServer")) {
736 smtptmp = stmp.substring(10); //length of MailServer
737 smtptmp = smtptmp.trim();
738 break;
739 }
740 }
741 maincfg.close();
742
743 //try if lookup fails
744 if(smtptmp.equals("NULL") || smtptmp.equals("null")) {
745 String email2=fromaddr;
746 if(!email2.equals("NULL") && !email2.equals("null")) {
747 int loc = email2.indexOf('@');
748 email2 = email2.substring(loc+1);
749 smtptmp = "mail."+email2;
750 }
751 }
752 if(!smtptmp.equals("NULL") && !smtptmp.equals("null")) {
753 collection.schedule_options.setValue("smtp", false, smtptmp);
754 }
755
756 }
757
758
759 private void createLockFile(File lock_file)
760 {
761 try {
762 Document default_lockfile = XMLTools.parseXMLFile("xml/" + LOCK_FILE, true);
763 String user_name = System.getProperty("user.name");
764 Element person_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
765 person_element.appendChild(default_lockfile.createTextNode(user_name));
766 person_element = null;
767 user_name = null;
768 String machine_name = Utility.getMachineName();
769 Element machine_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
770 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
771 machine_element = null;
772 machine_name = null;
773 String date_time = Utility.getDateString();
774 Element date_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
775 date_element.appendChild(default_lockfile.createTextNode(date_time));
776 date_element = null;
777 date_time = null;
778 XMLTools.writeXMLFile(lock_file, default_lockfile);
779 }
780 catch (Exception exception) {
781 DebugStream.printStackTrace(exception);
782 }
783 }
784
785
786 public boolean deleteCollection(String collection_name)
787 {
788 // First we must release the collection from the local library, if it's running
789 if (LocalLibraryServer.isRunning() == true) {
790 LocalLibraryServer.releaseCollection(collection_name);
791 }
792
793 // Delete the collection on the server if we're using a remote Greenstone
794 if (Gatherer.isGsdlRemote) {
795 Gatherer.remoteGreenstoneServer.deleteCollection(collection_name);
796 }
797
798 // if Greenstone3, need to deactivate the collection on the server
799 if (Gatherer.GS3) {
800 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection_name);
801 }
802
803 // Now delete the collection directory
804 return Utility.delete(new File(getCollectionDirectoryPath(collection_name)));
805 }
806
807
808 public void fireFileAddedToCollection(File file)
809 {
810 // Send the event off to all the CollectionContentsChangedListeners
811 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
812 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
813 }
814 }
815
816
817 /** Retrieve the current collection.
818 * @return The <strong>Collection</strong> itself.
819 */
820 public Collection getCollection() {
821 return collection;
822 }
823
824
825 /** Returns the absolute filename of the specified collection's directory.
826 */
827 static public String getCollectionDirectoryPath(String collection_name)
828 {
829 return Gatherer.getCollectDirectoryPath() + collection_name + File.separator;
830 }
831
832
833 /** Returns the absolute filename of the loaded collection's archives directory.
834 */
835 static public String getLoadedCollectionArchivesDirectoryPath()
836 {
837 return getLoadedCollectionDirectoryPath() + "archives" + File.separator;
838 }
839
840 /** Returns the absolute filename of the loaded collection's export directory.
841 */
842 static public String getLoadedCollectionExportDirectoryPath()
843 {
844 return getLoadedCollectionDirectoryPath() + "export" + File.separator;
845 }
846
847
848
849 /** Returns the absolute filename of the loaded collection's building directory.
850 */
851 static public String getLoadedCollectionBuildingDirectoryPath()
852 {
853 return getLoadedCollectionDirectoryPath() + "building" + File.separator;
854 }
855
856
857 /** Returns the absolute filename of the loaded collection's collect.cfg file.
858 */
859 static public String getLoadedCollectionCfgFilePath()
860 {
861 String path = (Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG;
862 return getLoadedCollectionEtcDirectoryPath() + path;
863 }
864
865
866 /** Returns the absolute filename of the loaded collection's directory.
867 */
868 static public String getLoadedCollectionDirectoryPath()
869 {
870 return collection.getCollectionDirectory().getPath() + File.separator;
871 }
872
873
874 /** Returns the absolute filename of the loaded collection's etc directory.
875 */
876 static public String getLoadedCollectionEtcDirectoryPath()
877 {
878 return getLoadedCollectionDirectoryPath() + "etc" + File.separator;
879 }
880
881
882 /** Returns the absolute filename of the loaded collection's .col file.
883 */
884 static public String getLoadedCollectionColFilePath()
885 {
886 return getLoadedCollectionDirectoryPath() + "gli.col";
887 }
888
889
890 /** Returns the absolute filename of the loaded collection's images directory.
891 */
892 static public String getLoadedCollectionImagesDirectoryPath()
893 {
894 return getLoadedCollectionDirectoryPath() + "images" + File.separator;
895 }
896
897
898 /** Returns the absolute filename of the loaded collection's import directory.
899 */
900 static public String getLoadedCollectionImportDirectoryPath()
901 {
902 return getLoadedCollectionDirectoryPath() + "import" + File.separator;
903 }
904
905
906 /** Returns the absolute filename of the loaded collection's index directory.
907 */
908 static public String getLoadedCollectionIndexDirectoryPath()
909 {
910 return getLoadedCollectionDirectoryPath() + "index" + File.separator;
911 }
912
913
914 /** Returns the absolute filename of the loaded collection's log directory.
915 */
916 static public String getLoadedCollectionLogDirectoryPath()
917 {
918 return getLoadedCollectionDirectoryPath() + "log" + File.separator;
919 }
920
921
922 /** Returns the absolute filename of the loaded collection's macros directory.
923 */
924 static public String getLoadedCollectionMacrosDirectoryPath()
925 {
926 return getLoadedCollectionDirectoryPath() + "macros" + File.separator;
927 }
928
929
930 /** Returns the absolute filename of the loaded collection's metadata directory.
931 */
932 static public String getLoadedCollectionMetadataDirectoryPath()
933 {
934 return getLoadedCollectionDirectoryPath() + "metadata" + File.separator;
935 }
936
937
938 /** Returns the (group-qualified) name of the loaded collection with
939 * OS-dependent file separator.
940 */
941 static public String getLoadedCollectionName()
942 {
943 return CollectionManager.getLoadedCollectionName(false);
944 }
945
946 /** Returns the (group-qualified) name of the loaded collection with
947 * OS-dependent space separator.
948 * @url true if url-type forward slashes, false if OS-dependent filesystem slashes.
949 */
950 static public String getLoadedCollectionName(boolean url)
951 {
952 if (collection != null) {
953 //return collection.getName();
954 return collection.getGroupQualifiedName(url);
955 }
956
957 return null;
958 }
959
960 /** @return the subname of any collection (stripped of any collection-group). */
961 static public String getLoadedCollectionTailName()
962 {
963 if (collection != null) {
964 return collection.getCollectionTailName();
965 }
966
967 return null;
968 }
969
970 /** Returns the "collectionGroupName/collectionName" or just the collectionName
971 * depending on whether the collection is part of a collection group or not.
972 * If url = true, then returns the sub-path as a URL (containing / only),
973 * and if url = false, then the sub-path is returned in filepath form
974 * (\ or /, depending on the OS).
975 */
976 static public String getLoadedGroupQualifiedCollectionName(boolean url)
977 {
978 if (collection != null) {
979 return collection.getGroupQualifiedName(url);
980 }
981
982 return null;
983 }
984
985 public CollectionTree getCollectionTree()
986 {
987 if (collection_tree == null) {
988 collection_tree = new CollectionTree(collection_tree_model, true);
989 }
990
991 return collection_tree;
992 }
993
994
995 /** Retrieve the tree model associated with the current collection. */
996 public CollectionTreeModel getCollectionTreeModel()
997 {
998 if (collection_tree_model == null && collection != null) {
999 // Use the import directory to generate a new CollectionTreeModel
1000 collection_tree_model = new CollectionTreeModel(new CollectionTreeNode(new File(getLoadedCollectionImportDirectoryPath())));
1001 // Ensure that the manager is a change listener for the tree.
1002 if (fm_tree_model_listener == null) {
1003 fm_tree_model_listener = new FMTreeModelListener();
1004 }
1005 collection_tree_model.addTreeModelListener(fm_tree_model_listener);
1006 }
1007 return collection_tree_model;
1008 }
1009
1010
1011 /** This method when called, creates a new GShell in order to run the import.pl script.
1012 * @see org.greenstone.gatherer.Configuration
1013 * @see org.greenstone.gatherer.Gatherer
1014 * @see org.greenstone.gatherer.gui.BuildOptions
1015 * @see org.greenstone.gatherer.shell.GShell
1016 * @see org.greenstone.gatherer.shell.GShellListener
1017 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
1018 * @see org.greenstone.gatherer.util.Utility
1019 */
1020 public void importCollection() {
1021 importing = true;
1022
1023 if (!saved()) {
1024 DebugStream.println("CollectionManager.importCollection().forcesave");
1025 import_monitor.saving();
1026 saveCollection();
1027 }
1028
1029 DebugStream.println("CollectionManager.importCollection()");
1030 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
1031 //check that we can remove the old index before starting import
1032 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
1033 if (index_dir.exists()) {
1034 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
1035 if (!canDelete(index_dir)) {
1036 // tell the user
1037 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1038 // tell the gui manager
1039 // a message for the building log
1040 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
1041 Gatherer.g_man.create_pane.message(event);
1042 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
1043 Gatherer.g_man.create_pane.processComplete(event);
1044 importing = false;
1045 return;
1046 }
1047 }
1048
1049 // Generate the import.pl command
1050 ArrayList command_parts_list = new ArrayList();
1051 if (!Gatherer.isGsdlRemote) {
1052 command_parts_list.add(Configuration.perl_path);
1053 command_parts_list.add("-S");
1054 }
1055
1056 if (Configuration.fedora_info.isActive()) {
1057 command_parts_list.add(scriptPath + "g2f-import.pl");
1058
1059 command_parts_list.add("-hostname");
1060 command_parts_list.add(Configuration.fedora_info.getHostname());
1061
1062 command_parts_list.add("-port");
1063 command_parts_list.add(Configuration.fedora_info.getPort());
1064
1065 command_parts_list.add("-username");
1066 command_parts_list.add(Configuration.fedora_info.getUsername());
1067
1068 command_parts_list.add("-password");
1069 command_parts_list.add(Configuration.fedora_info.getPassword());
1070
1071 command_parts_list.add("-protocol");
1072 command_parts_list.add(Configuration.fedora_info.getProtocol());
1073 }
1074 else {
1075 String cmdPrefix = null;
1076 if ( CollectionDesignManager.isCompleteBuild() ) {
1077 cmdPrefix = "full-";
1078 CollectionDesignManager.setImportWasFull( true );
1079 } else {
1080 cmdPrefix = "incremental-";
1081 CollectionDesignManager.setImportWasFull( false );
1082 }
1083 command_parts_list.add(scriptPath + cmdPrefix + "import.pl"); // scriptPath already set according to local or remote case
1084 }
1085
1086 command_parts_list.add("-gli");
1087 command_parts_list.add("-language");
1088 command_parts_list.add(Configuration.getLanguage());
1089 if(!Gatherer.isGsdlRemote) {
1090 command_parts_list.add("-collectdir");
1091 command_parts_list.add(getCollectDirectory());
1092 }
1093
1094 String[] import_options = collection.import_options.getValues();
1095 for (int i = 0; i < import_options.length; i++) {
1096 System.err.println( "Tacking on option: " + import_options[i] );
1097 command_parts_list.add(import_options[i]);
1098 }
1099
1100 command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
1101
1102 // Run the import.pl command
1103 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1104 GShell shell = new GShell(command_parts, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
1105 //shell.setEventProperty("is_incremental", Boolean.toString(is_incremental));
1106 shell.addGShellListener(Gatherer.g_man.create_pane);
1107 shell.addGShellListener(Gatherer.g_man.format_pane);
1108 shell.start();
1109 DebugStream.println("CollectionManager.importCollection().return");
1110
1111 importing = false;
1112 }
1113
1114
1115 public void importMetadataSet(MetadataSet external_metadata_set)
1116 {
1117 // Copy the .mds file into the collection's "metadata" folder...
1118 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
1119
1120 // ...but not if it is the redundant "hidden.mds" file
1121 if (external_metadata_set_file.getName().equals("hidden.mds")) {
1122 return;
1123 }
1124
1125 // ...and only if it doesn't already exist
1126 File metadata_set_file = new File(getLoadedCollectionMetadataDirectoryPath(), external_metadata_set_file.getName());
1127 if (!metadata_set_file.exists()) {
1128 try {
1129 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, false);
1130
1131 // If we're using a remote Greenstone server, upload the metadata file
1132 if (Gatherer.isGsdlRemote) {
1133 Gatherer.remoteGreenstoneServer.uploadCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1134 }
1135 }
1136 catch (Exception exception) {
1137 DebugStream.printStackTrace(exception);
1138 }
1139
1140 // Load it into the MetadataSetManager
1141 MetadataSetManager.loadMetadataSet(metadata_set_file);
1142 }
1143 }
1144
1145
1146 /** Determine if we are currently in the middle of importing (and thus, in this case, we can't allow the log writer to exit). Boy was this a mission to track down. The cascade of crap rolls out something like this: Joe Schmo clicks 'Build Collection', which calls the importCollection() method above, which in turn saves the collection with a saveTask, which fires a collectionChanged message once its finished, which drives the list of logs shown on the create pane to update, which fires a itemChanged() event to the OptionsPane who dutifully tells the current log writer thread to finish up writing (all zero lines its been asked to write) and then die. Wereapon Joe Schmo gets a pretty log to look at, but it isn't actually being written to file so the next time he tries to view it faeces hits the air motion cooling device. Joy.
1147 * @return true if the gli is currently importing
1148 */
1149 public boolean isImporting() {
1150 return importing;
1151 }
1152
1153
1154 public void loadCollection(String collection_file_path)
1155 {
1156 // Display a modal progress popup to indicate that the collection is being loaded
1157 ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"), Dictionary.get("CollectionManager.Loading_Collection_Please_Wait"));
1158 load_collection_progress_popup.display();
1159
1160 // Load the collection on a separate thread so the progress bar updates correctly
1161 (new LoadCollectionTask(collection_file_path, load_collection_progress_popup)).start();
1162 }
1163
1164
1165 private class LoadCollectionTask
1166 extends Thread
1167 {
1168 private String collection_file_path = null;
1169 private ModalProgressPopup load_collection_progress_popup = null;
1170
1171 public LoadCollectionTask(String collection_file_path, ModalProgressPopup load_collection_progress_popup)
1172 {
1173 this.collection_file_path = collection_file_path;
1174 this.load_collection_progress_popup = load_collection_progress_popup;
1175 }
1176
1177 public void run()
1178 {
1179 loadCollectionInternal(collection_file_path);
1180 load_collection_progress_popup.close();
1181 Gatherer.setMenuBarEnabled(true);
1182 }
1183 }
1184
1185
1186 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
1187 * @param location The path to the collection as a <strong>String</strong>.
1188 * @see org.greenstone.gatherer.Configuration
1189 * @see org.greenstone.gatherer.Gatherer
1190 * @see org.greenstone.gatherer.collection.Collection
1191 * @see org.greenstone.gatherer.util.Utility
1192 */
1193 private void loadCollectionInternal(String location)
1194 {
1195 DebugStream.println("Loading collection " + location + "...");
1196
1197
1198 // Check we have actually been given a .col file.
1199 if (!location.endsWith(".col")) {
1200 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1201 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
1202 return;
1203 }
1204
1205 // Check that the collection configuration file is available
1206 File collection_file = new File(location);
1207
1208 File collection_directory = collection_file.getParentFile();
1209 String collection_name = collection_directory.getName();
1210
1211 if (Gatherer.isGsdlRemote) {
1212 if (Gatherer.remoteGreenstoneServer.downloadCollection(collection_name).equals("")) {
1213 return;
1214 }
1215 }
1216
1217 // Ensure that the collection directory exists
1218 if (collection_directory == null || !collection_directory.exists()) {
1219 // We can't open this
1220 System.err.println("CollectionManager.loadCollection: No collection directory.");
1221 return;
1222 }
1223
1224 String file_str = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
1225 File collection_config_file = new File(collection_directory, file_str);
1226 if (!collection_config_file.exists()) {
1227 System.err.println("CollectionManager.loadCollection: No config file.");
1228 collection_directory = null;
1229 collection_config_file = null;
1230 return;
1231 }
1232
1233 // Ensure that an import directory exists for this collection
1234 File collection_import_directory = new File(collection_directory, "import");
1235 if (!collection_import_directory.exists()) {
1236 collection_import_directory.mkdir();
1237 }
1238
1239 // Special case of a user trying to open an old greenstone collection.
1240 boolean non_gli_collection = false;
1241 File collection_metadata_directory = new File(collection_directory, "metadata");
1242 if (!collection_metadata_directory.exists()) {
1243 DebugStream.println("Loading non-gatherer collection...");
1244 // Show a warning message in case user wants to quit now
1245 non_gli_collection = true;
1246 WarningDialog legacy_dialog = new WarningDialog("warning.LegacyCollection", Dictionary.get("LegacyCollection.Title"), Dictionary.get("LegacyCollection.Message"), null, true);
1247 if (legacy_dialog.display()==JOptionPane.CANCEL_OPTION) {
1248 legacy_dialog.dispose();
1249 collection_directory = null;
1250 collection_config_file = null;
1251 return;
1252 }
1253 legacy_dialog.dispose();
1254
1255 }
1256
1257 // Now determine if a lock already exists on this collection.
1258 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
1259 if (lock_file.exists()) {
1260 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, collection_name, lock_file);
1261 int choice = dialog.getChoice();
1262 dialog.dispose();
1263 dialog = null;
1264
1265 if (choice != LockFileDialog.YES_OPTION) {
1266 // user has cancelled
1267 lock_file = null;
1268 collection_directory = null;
1269 collection_config_file = null;
1270 return;
1271 }
1272
1273 lock_file.delete();
1274 }
1275
1276 // now we are using gli.col - old colls may have used the collection name
1277 if (!collection_file.exists()) {
1278 File old_coll_file = new File(collection_directory, collection_name+".col");
1279 if (old_coll_file.exists()) {
1280 try {
1281 old_coll_file.renameTo(collection_file);
1282 } catch (Exception e) {
1283 DebugStream.println("Couldn't rename "+old_coll_file.getName()+" to gli.col. Will just carry on with default gli.col");
1284 // but just carry on.
1285 }
1286 }
1287 }
1288
1289 try {
1290 // Create a lock file.
1291 createLockFile(lock_file);
1292 // This lock file may not have been created so check
1293 if(!lock_file.canWrite()) {
1294 // The lock file cannot be written to. Most likely cause incorrect file permissions.
1295 System.err.println("Cannot write lock file!");
1296 String args[] = new String[2];
1297 args[0] = location;
1298 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{lock_file.getAbsolutePath()});
1299 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS")!=-1){
1300 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1301 args[1] += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1302 //}
1303 }
1304 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1305 args = null;
1306 return;
1307 }
1308
1309 // need to fix this up as currently it craps out if the .col file is not there, which is may not always be.
1310 if (canDoScheduling() && collection_file.exists()) {
1311 //THIS LOOKS LIKE THE BEST PLACE TO TRY AND UPDATE .col FILES FOR EXISTING COLLECTIONS...Wendy
1312 // Don't need to update anything if collection_file doesn't exist yet.
1313 //First, see if "Schedule" exists in the XMl File...
1314 BufferedReader bir = new BufferedReader(new FileReader(collection_file));
1315 boolean flag = false;
1316 try {
1317 String stmp = new String();
1318
1319 while((stmp = bir.readLine()) != null) {
1320 stmp = stmp.trim();
1321 if(stmp.equals("<Schedule>") || stmp.equals("<Schedule/>")) {
1322 flag = true;
1323 break;
1324 }
1325 }
1326 bir.close();
1327
1328 } catch (IOException ioe) {
1329 DebugStream.printStackTrace(ioe);
1330 }
1331
1332 //modify if old .col (i.e. no Schedule exists in XML file)
1333 if(!flag) {
1334 File new_collection_file = new File(collection_directory.getAbsolutePath() + "/tmp.col");
1335
1336
1337 BufferedWriter bor = new BufferedWriter(new FileWriter(new_collection_file));
1338 bir = new BufferedReader(new FileReader(collection_file));
1339
1340 try {
1341 String stmp = new String();
1342 while((stmp = bir.readLine()) != null) {
1343 String stmp2 = stmp.trim();
1344 if(stmp2.startsWith("<!ELEMENT Argument")) {
1345 bor.write(" <!ELEMENT Schedule (Arguments*)>\n");
1346 }
1347 else if(stmp2.equals("</BuildConfig>")) {
1348 bor.write(" <Schedule/>\n");
1349 }
1350
1351 bor.write(stmp + "\n");
1352
1353 }
1354 bir.close();
1355 bor.close();
1356 } catch (IOException ioe) {
1357 DebugStream.printStackTrace(ioe);
1358 }
1359
1360 //copy over tmp.col to replace
1361 try {
1362 collection_file.delete();
1363 new_collection_file.renameTo(collection_file);
1364 } catch (Exception e) {
1365 DebugStream.printStackTrace(e);
1366 }
1367 }
1368 }
1369
1370 // Open the collection file
1371 this.collection = new Collection(collection_file);
1372 if (collection.error) {
1373 collection = null;
1374 // Remove lock file
1375 if (lock_file.exists()) {
1376 lock_file.delete();
1377 }
1378 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
1379 }
1380
1381 if (canDoScheduling()) {
1382 scheduling();
1383 }
1384
1385 // These may have been set in the past, but are no longer used
1386 // by GLI
1387 collection.import_options.removeValue("removeold");
1388 collection.import_options.removeValue("keepold");
1389
1390 MetadataSetManager.clearMetadataSets();
1391 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
1392
1393 // Make sure we always have the extracted metadata set
1394 addRequiredMetadataSets();
1395
1396 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
1397
1398 // If this is a non-GLI (legacy) collection, load the default metadata sets
1399 if (non_gli_collection) {
1400 addDefaultMetadataSets();
1401
1402 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
1403 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
1404 }
1405
1406 // Read through the metadata.xml files in the import directory, building up the metadata value trees
1407 MetadataXMLFileManager.clearMetadataXMLFiles();
1408 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory,collection.toSkimFile());
1409
1410
1411 // get rid of the previous scan through docxml files
1412 DocXMLFileManager.clearDocXMLFiles();
1413
1414 if (Configuration.fedora_info.isActive()) { // FLI case
1415 // Read through the docmets.xml files in the export directory
1416 File collection_export_directory = new File(getLoadedCollectionExportDirectoryPath());
1417 DocXMLFileManager.loadDocXMLFiles(collection_export_directory,"docmets.xml");
1418 }
1419 else {
1420 // Read through the doc.xml files in the archives directory
1421 File collection_archives_directory = new File(getLoadedCollectionArchivesDirectoryPath());
1422 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory,"doc.xml");
1423 }
1424
1425
1426 // Get a list of the collection specific classifiers and plugins
1427 Classifiers.loadClassifiersList(collection_name);
1428 Plugins.loadPluginsList(collection_name);
1429
1430 collection.cdm = new CollectionDesignManager(collection_config_file);
1431 if (non_gli_collection) {
1432 // Change the classifiers to use the namespaced element names
1433 LegacyCollectionImporter.updateClassifiers(collection.cdm);
1434 }
1435
1436 // We're done. Let everyone know.
1437 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", collection_name));
1438 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
1439 }
1440 catch (Exception error) {
1441 // There is obviously no existing collection present.
1442 DebugStream.printStackTrace(error);
1443 error.printStackTrace();
1444 if(error.getMessage() != null) {
1445 String[] args = new String[2];
1446 args[0] = location;
1447 args[1] = error.getMessage();
1448 //args[1] = "The Librarian Interface does not have permission to write to... Please check file permissions.";
1449 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1450 }
1451 else {
1452 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1453 }
1454 }
1455
1456 lock_file = null;
1457 collection_directory = null;
1458 collection_config_file = null;
1459 }
1460
1461 /** At present, scheduling only works for GS2, only when GS2 is local and only when GLI runs from
1462 * within a GS2 installation. This method can be adjusted as scheduling becomes available for more
1463 * more situations. */
1464 public static boolean canDoScheduling() {
1465 // Would be nice to support more of these, rather than returning false
1466 if(Gatherer.isGsdlRemote) {
1467 return false;
1468 }
1469 if(Gatherer.GS3) {
1470 return false;
1471 }
1472 if (Configuration.fedora_info.isActive()) {
1473 return false;
1474 }
1475
1476 // GS2's etc/main.cfg is necessary for scheduling, but scheduling looks for it locally:
1477 // it assumes GLI is inside a GS2 installation
1478 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
1479 if(!mcfg.exists()) {
1480 System.out.println("Cannot do scheduling, since there is no file: " + mcfg.getAbsolutePath()
1481 + ".\nScheduling presently depends on GLI running from inside a GS2.");
1482 return false;
1483 }
1484
1485 return true;
1486 }
1487
1488 private void makeCollection(String name, String email)
1489 {
1490 // Generate the mkcol.pl command
1491 ArrayList command_parts_list = new ArrayList();
1492 if (!Gatherer.isGsdlRemote) {
1493 command_parts_list.add(Configuration.perl_path);
1494 command_parts_list.add("-S");
1495 }
1496 command_parts_list.add(scriptPath + "mkcol.pl");
1497 if(Gatherer.GS3) {
1498 command_parts_list.add(Utility.GS3MODE_ARGUMENT); // add '-gs3mode'
1499 }
1500 if(!Gatherer.isGsdlRemote) {
1501 command_parts_list.add("-collectdir");
1502 command_parts_list.add(getDefaultCollectDirectory());
1503 }
1504 command_parts_list.add("-win31compat");
1505 command_parts_list.add((Gatherer.isGsdlRemote) ? "false" : "true");
1506
1507 if (email != null && !email.equals("")) {
1508 command_parts_list.add("-creator");
1509 command_parts_list.add(email);
1510 }
1511
1512 command_parts_list.add(name);
1513
1514 // Run the mkcol.pl command
1515 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1516 //for(int i = 0; i < command_parts.length; i++) {
1517 ///ystem.err.println("\""+command_parts[i]+"\"");
1518 //}
1519
1520 GShell process = new GShell(command_parts, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1521 process.run(); // Don't bother threading this... yet
1522 }
1523
1524
1525 /** Any implementation of GShellListener must include this method to allow the GShell to send messages to listeners. However in this case the CollectionManager is in no way interested in what the messages are, just the import events which have a specific type and are handled elsewhere. Thus we can safely ignore this event.
1526 * @param event A <strong>GShellEvent</strong> which contains a the message.
1527 */
1528 public synchronized void message(GShellEvent event) {
1529
1530 }
1531
1532
1533 public void metadataChanged(CollectionTreeNode[] file_nodes)
1534 {
1535 if (collection != null) {
1536 collection.setMetadataChanged(true);
1537 }
1538 }
1539
1540
1541 public void openCollectionFromLastTime() {
1542 // If there was an open collection last session, reopen it
1543 if (Gatherer.open_collection_file_path != null) {
1544 // Load the collection now
1545 loadCollection(Gatherer.open_collection_file_path);
1546 }
1547
1548 }
1549
1550
1551 /** This call is fired whenever a process within a GShell created by this class begins.
1552 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1553 * @see org.greenstone.gatherer.Gatherer
1554 * @see org.greenstone.gatherer.gui.GUIManager
1555 * @see org.greenstone.gatherer.shell.GShell
1556 */
1557 public synchronized void processBegun(GShellEvent event) {
1558 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
1559 ///ystem.err.println("ProcessBegun " + event.getType());
1560 // If this is one of the types where we wish to lock user control
1561 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1562 }
1563 /** This call is fired whenever a process within a GShell created by this class ends.
1564 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1565 * @see org.greenstone.gatherer.Gatherer
1566 * @see org.greenstone.gatherer.gui.GUIManager
1567 * @see org.greenstone.gatherer.shell.GShell
1568 */
1569 public synchronized void processComplete(GShellEvent event) {
1570 //ystem.err.println("CollectionManager.processComplete(" + event.getType() + ")");
1571 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1572 ///ystem.err.println("Received process complete event - " + event);
1573 // If we were running an import, now run a build.
1574 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1575 // Finish import.
1576 collection.setImported(true);
1577 collection.setFilesChanged(false);
1578 collection.setMetadataChanged(false);
1579 buildCollection();
1580 }
1581 else if(event.getType() == GShell.SCHEDULE && event.getStatus() == GShell.OK ) {
1582
1583 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.ScheduleBuilt", Dictionary.get("ScheduleBuilt.Title"), Dictionary.get("ScheduleBuilt.Message"), null, false);
1584 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1585 collection_built_warning_dialog.display();
1586 collection_built_warning_dialog.dispose();
1587 collection_built_warning_dialog = null;
1588 }
1589 // If we were running a build, now is when we move files across.
1590 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1591
1592 if ( CollectionDesignManager.buildcolWasFull() ) {
1593 if(installCollection()) {
1594 // If we have a local library running then ask it to add our newly create collection
1595 if (LocalLibraryServer.isRunning() == true) {
1596 LocalLibraryServer.addCollection(collection.getName());
1597 }
1598 else if (Gatherer.GS3) {
1599 //xiao comment out this: convertToGS3Collection();
1600 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1601 }
1602
1603 // Fire a collection changed first to update the preview etc buttons
1604 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1605
1606 // Now display a message dialog saying its all built
1607 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", Dictionary.get("CollectionBuilt.Title"), Dictionary.get("CollectionBuilt.Message"), null, false);
1608 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1609 collection_built_warning_dialog.display();
1610 collection_built_warning_dialog.dispose();
1611 collection_built_warning_dialog = null;
1612
1613 //Set nothing as needing rebuilding, as a build has just finished :-)
1614 CollectionDesignManager.resetRebuildTypeRequired();
1615 }
1616 else {
1617 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1618 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1619 DebugStream.println("Status is ok but !installCollection()");
1620 }
1621 }
1622 }
1623 else if (event.getStatus() == GShell.CANCELLED) {
1624 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Build_Cancelled"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1625 Gatherer.g_man.repaint();
1626 }
1627 else if (event.getStatus() == GShell.ERROR) {
1628 if (event.getType() == GShell.NEW) {
1629 String name = event.getMessage();
1630 String collectDir = getCollectionDirectoryPath(name);
1631 String errMsg = "";
1632 if (!new File(getCollectionDirectoryPath(name)).exists() || !new File(getCollectionDirectoryPath(name)).canWrite()) {
1633 String reason = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{collectDir});
1634 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection_With_Reason", new String[]{reason});
1635 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS") != -1){
1636 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1637 errMsg += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1638 //}
1639 }
1640 } else {
1641 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection");
1642 }
1643 JOptionPane.showMessageDialog(Gatherer.g_man, errMsg, Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1644 }
1645 else if(event.getType() == GShell.SCHEDULE) {
1646 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Schedule_Failed"), Dictionary.get("CollectionManager.Schedule_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1647 }
1648 else {
1649 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1650 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1651 }
1652
1653 Gatherer.g_man.repaint(); // It appears Java's own dialogs have the same not always painting correct area bug that I suffer from. Well I don't suffer from it personally, but my ModalDialog components do.
1654 }
1655 }
1656
1657
1658 /** Determine if the manager is ready for actions apon its collection.
1659 * @return A <i>boolean</i> which is <i>true</i> to indicate a collection has been loaded and thus the collection is ready for editing, <i>false</i> otherwise.
1660 */
1661 static public synchronized boolean ready() {
1662 if(collection != null) {
1663 return true;
1664 }
1665 else {
1666 return false;
1667 }
1668 }
1669
1670
1671 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1672 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1673 */
1674 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1675 build_monitor = monitor;
1676 }
1677 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1678 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1679 */
1680 public void registerImportMonitor(GShellProgressMonitor monitor) {
1681 import_monitor = monitor;
1682 }
1683
1684 public void registerScheduleMonitor(GShellProgressMonitor monitor) {
1685 schedule_monitor = monitor;
1686 }
1687
1688
1689 static public void removeCollectionContentsChangedListener(CollectionContentsChangedListener listener)
1690 {
1691 collection_contents_changed_listeners.remove(listener);
1692 }
1693
1694
1695 public void removeMetadataSet(MetadataSet metadata_set)
1696 {
1697 DebugStream.println("Removing metadata set...");
1698
1699 // Delete the .mds file from the collection's "metadata" folder...
1700 File metadata_set_file = metadata_set.getMetadataSetFile();
1701
1702 // ...but not if it is the "ex.mds" file
1703 if (metadata_set_file.getName().equals("ex.mds")) {
1704 return;
1705 }
1706
1707 // ...and only if it exists
1708 if (metadata_set_file.exists()) {
1709 metadata_set_file.delete();
1710
1711 // Unload it from the MetadataSetManager
1712 MetadataSetManager.unloadMetadataSet(metadata_set);
1713
1714 // If we're using a remote Greenstone server, delete the metadata file on the server
1715 if (Gatherer.isGsdlRemote) {
1716 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1717 }
1718 }
1719 }
1720
1721
1722 /** Used to check whether all open collections have a 'saved' state.
1723 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1724 * @see org.greenstone.gatherer.collection.Collection
1725 */
1726 public boolean saved() {
1727 boolean result = true;
1728 if(collection != null) {
1729 result = collection.getSaved();
1730 }
1731 return result;
1732 }
1733
1734
1735 /** Saves the currently loaded collection. */
1736 public void saveCollection()
1737 {
1738
1739 if (collection == null) return;
1740
1741 DebugStream.println("Saving collection " + collection.getName() + "...");
1742
1743 // Change cursor to hourglass
1744 Gatherer.g_man.wait(true);
1745
1746 // Create a backup of the collection file, just in case anything goes wrong
1747 File collection_file = new File(getLoadedCollectionColFilePath());
1748 if (collection_file.exists()) {
1749 File collection_file_backup = new File(collection_file.getAbsolutePath() + "~");
1750 if (!collection_file.renameTo(collection_file_backup)) {
1751 DebugStream.println("Error in CollectionManager.saveCollection(): could not create backup file.");
1752 }
1753 collection_file_backup.deleteOnExit();
1754 }
1755
1756 // Write out the collection file
1757 collection.save();
1758
1759 // Write out the collection configuration file
1760 collection.cdm.save();
1761
1762 // Change cursor back to normal
1763 Gatherer.g_man.wait(false);
1764 }
1765
1766
1767 /** I started giving the user the choice of using an existing meta set or creating a new one. The second option being so that they didn't have to add/merge/ignore each element, they could all be added automatically. However, I am not sure where the merge prompt gets called from, and it is not essential, so I am leaving it for now - it should be added back in and finished. [kjdon] */
1768 // now add in greenstone metadata set too.
1769 private void addDefaultMetadataSets()
1770 {
1771 // Add dublin core which is the default metadata set. The user
1772 // can change this later
1773 File dc_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"dublin.mds");
1774 if (dc_file.exists()) {
1775 importMetadataSet(new MetadataSet(dc_file));
1776 }
1777 File gs_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"greenstone.mds");
1778 if (gs_file.exists()) {
1779 importMetadataSet(new MetadataSet(gs_file));
1780 }
1781 }
1782
1783
1784 private void addRequiredMetadataSets()
1785 {
1786 // Always import the extracted metadata set
1787 File extracted_metadata_set_file = new File(Gatherer.getGLIMetadataDirectoryPath() + MetadataSetManager.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
1788 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
1789 }
1790
1791 private String getDefaultCollectDirectory() {
1792 String collect_dir = Gatherer.getCollectDirectoryPath();
1793 // Remove erroneous file windows file separator as it causes problems when running import.pl
1794 if(collect_dir.length() > 2 && collect_dir.endsWith("\\")) {
1795 collect_dir = collect_dir.substring(0, collect_dir.length() - 1);
1796 }
1797 return collect_dir;
1798 }
1799
1800 // used as arg in the perl scripts
1801 private String getCollectDirectory() {
1802 return Gatherer.getCollectDirectoryPath();
1803
1804 // the following will stick any colgroup at the end of the collect directory, making it no longer
1805 // possible to get the real collect dir in a general manner if this were located outside greenstone
1806 //String collect_dir = collection.getCollectionDirectory().getParentFile().getPath();
1807 //return collect_dir;
1808 }
1809
1810
1811 /** Install collection by moving its files from building to index after a successful build.
1812 * @see org.greenstone.gatherer.Gatherer
1813 * @see org.greenstone.gatherer.util.Utility
1814 */
1815 private boolean installCollection()
1816 {
1817 if (Configuration.fedora_info.isActive()) {
1818 DebugStream.println("Fedora build complete. No need to move files.");
1819 return true;
1820 }
1821
1822
1823 DebugStream.println("Build complete. Moving files.");
1824
1825 try {
1826 // Ensure that the local library has released this collection so we can delete the index directory
1827 if (LocalLibraryServer.isRunning() == true) {
1828 LocalLibraryServer.releaseCollection(collection.getName());
1829 }
1830 // deactivate it in tomcat so that windows will release the index files
1831 if (Gatherer.GS3 && !Gatherer.isGsdlRemote) {
1832 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection.getName());
1833 }
1834 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
1835 DebugStream.println("Index = " + index_dir.getAbsolutePath());
1836
1837 File building_dir = new File(getLoadedCollectionBuildingDirectoryPath());
1838 DebugStream.println("Building = " + building_dir.getAbsolutePath());
1839
1840 // Get the build mode from the build options
1841 String build_mode = collection.build_options.getValue("mode");
1842
1843 // Special case for build mode "all": replace index dir with building dir
1844 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
1845 // Remove the old index directory
1846 if (index_dir.exists()) {
1847 Utility.delete(index_dir);
1848
1849 // Wait for a couple of seconds, just for luck
1850 wait(2000);
1851
1852 // Check the delete worked
1853 if (index_dir.exists()) {
1854 throw new Exception(Dictionary.get("CollectionManager.Index_Not_Deleted"));
1855 }
1856 }
1857
1858 if (Gatherer.isGsdlRemote) {
1859 Gatherer.remoteGreenstoneServer.deleteCollectionFile(
1860 collection.getGroupQualifiedName(false), new File(getLoadedCollectionIndexDirectoryPath()));
1861 Gatherer.remoteGreenstoneServer.moveCollectionFile(collection.getGroupQualifiedName(false),
1862 new File(getLoadedCollectionBuildingDirectoryPath()), new File(getLoadedCollectionIndexDirectoryPath()));
1863 }
1864
1865 // Move the building directory to become the new index directory
1866 if (building_dir.renameTo(index_dir) == false) {
1867 throw new Exception(Dictionary.get("CollectionManager.Build_Not_Moved"));
1868 }
1869 }
1870
1871 // Otherwise copy everything in the building dir into the index dir
1872 else {
1873 moveContentsInto(building_dir, index_dir);
1874 }
1875 }
1876 catch (Exception exception) {
1877 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Install_Exception", exception.getMessage()), "Error", JOptionPane.ERROR_MESSAGE);
1878 return false;
1879 }
1880 return true;
1881 }
1882
1883
1884 /** Moves all the files in one directory into another, overwriting existing files */
1885 private void moveContentsInto(File source_directory, File target_directory)
1886 {
1887 File[] source_files = source_directory.listFiles();
1888 for (int i = 0; i < source_files.length; i++) {
1889 File source_file = source_files[i];
1890 File target_file = new File(target_directory, source_file.getName());
1891
1892 if (source_file.isDirectory()) {
1893 moveContentsInto(source_file, target_file);
1894 source_file.delete();
1895 }
1896 else {
1897 if (target_file.exists()) {
1898 target_file.delete();
1899 }
1900
1901 source_file.renameTo(target_file);
1902 }
1903 }
1904 }
1905
1906 private void updateCollectionConfigXML(File base_cfg, File new_cfg) {
1907 //In this method, the files base_cfg and new_cfg are all xml files.
1908
1909 Document base_cfg_doc = XMLTools.parseXMLFile(base_cfg);
1910 XMLTools.writeXMLFile(new_cfg, base_cfg_doc);
1911 Document new_cfg_doc = XMLTools.parseXMLFile(new_cfg);
1912 Element collection_config = new_cfg_doc.getDocumentElement();
1913
1914 Node browseNode = XMLTools.getChildByTagNameIndexed(collection_config, StaticStrings.BROWSE_STR, 0);
1915 NodeList classifier_children = ((Element)browseNode).getElementsByTagName(StaticStrings.CLASSIFIER_STR);
1916 int num_nodes = classifier_children.getLength();
1917
1918 if (num_nodes < 1) {
1919 return;
1920 }
1921
1922 // Read in the classifier command watching for hfile, metadata and sort arguments.
1923 String buttonname = null;
1924 String hfile = null;
1925 String metadata = null;
1926 String sort = null;
1927
1928 for (int i=0; i<num_nodes; i++) {
1929 Element classifier_element = (Element)classifier_children.item(i);
1930 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
1931 for (int j=0; j<option_children.getLength(); j++) {
1932 Element option_element = (Element)option_children.item(j);
1933 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
1934 String value_str = option_element.getAttribute(StaticStrings.VALUE_ATTRIBUTE);
1935
1936 if (name_str == null || name_str.equals("")) {
1937 continue;
1938 }
1939 if (name_str != null && value_str == null ) {
1940 value_str = "";
1941 }
1942 if (name_str.equals("hfile")) {
1943 hfile = value_str;
1944 }
1945 else if (name_str.equals("metadata") && value_str != null) {
1946 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
1947 if (replacement != null && !replacement.equals("")) {
1948 metadata = replacement;
1949 }
1950 }
1951 else if (name_str.equals("sort") && value_str != null) {
1952 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
1953 if (replacement != null && !replacement.equals("")) {
1954 sort = replacement;
1955 }
1956 }
1957 else if(name_str.equals("buttonname") && value_str != null) {
1958 buttonname = value_str;
1959 }
1960 }
1961 }
1962 for (int i=0; i<num_nodes; i++) {
1963 Element classifier_element = (Element)classifier_children.item(i);
1964 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
1965 for (int j=0; j<option_children.getLength(); j++) {
1966 Element option_element = (Element)option_children.item(j);
1967 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
1968
1969 if (name_str.equals("metadata") && metadata != null) {
1970 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
1971 }
1972 else if (name_str.equals("hfile") && hfile != null) {
1973 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata + ".txt");
1974 }
1975 else if (name_str.equals("sort") && sort != null) {
1976 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, sort);
1977 }
1978 else if(name_str.equals("buttonname") && (buttonname == "" || buttonname == null)) {
1979 // No buttonname has been specified. Lets create one using the metadata as its value
1980 Element option = new_cfg_doc.createElement(StaticStrings.OPTION_STR);
1981 option.setAttribute(StaticStrings.NAME_ATTRIBUTE, "buttonname");
1982 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
1983 classifier_element.appendChild(option);
1984 }
1985 }
1986 }
1987 }
1988
1989 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title)
1990 {
1991 boolean first_name = true;
1992 boolean first_extra = true;
1993
1994 // Now read in base_cfg line by line, parsing important onces and/or replacing them with information pertinent to our collection. Each line is then written back out to the new collect.cfg file.
1995 try {
1996 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(base_cfg), "UTF-8"));
1997 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new_cfg), "UTF-8"));
1998 String command = null;
1999 while((command = in.readLine()) != null) {
2000 if (command.length()==0) {
2001 // output a new line
2002 out.newLine();
2003 continue;
2004 }
2005 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
2006 while(command.trim().endsWith("\\")) {
2007 command = command.substring(0, command.lastIndexOf("\\"));
2008 String next_line = in.readLine();
2009 if(next_line != null) {
2010 command = command + next_line;
2011 }
2012 }
2013 // commands can extend over more than one line so use the CommandTokenizer which takes care of that
2014 CommandTokenizer tokenizer = new CommandTokenizer(command, in, false);
2015 String command_type_str = tokenizer.nextToken().toLowerCase();
2016
2017 if (command_type_str.equals(StaticStrings.COLLECTIONMETADATA_STR)) {
2018 // read the whole thing in, but for collectionname, collectionextra, iconcollection, iconcollectionsmall we will ignore them
2019 StringBuffer new_command = new StringBuffer(command_type_str);
2020 String meta_name = tokenizer.nextToken();
2021 new_command.append(' ');
2022 new_command.append(meta_name);
2023 while (tokenizer.hasMoreTokens()) {
2024 new_command.append(' ');
2025 new_command.append(tokenizer.nextToken());
2026 }
2027 if (meta_name.equals(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR) || meta_name.equals(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR) || meta_name.equals(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR) || meta_name.equals(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR)) {
2028 // dont save
2029 } else {
2030 write(out, new_command.toString());
2031 }
2032 new_command = null;
2033 continue;
2034 } // if collectionmeta
2035
2036 if (command_type_str.equals("classify")) {
2037 StringBuffer text = new StringBuffer(command_type_str);
2038 // Read in the classifier command watching for hfile, metadata and sort arguments.
2039 String buttonname = null;
2040 String hfile = null;
2041 String new_metadata = null;
2042 String old_metadata = null;
2043
2044 while(tokenizer.hasMoreTokens()) {
2045 String token = tokenizer.nextToken();
2046 if (token.equals("-hfile")) {
2047 if(tokenizer.hasMoreTokens()) {
2048 text.append(" ");
2049 text.append(token);
2050 token = tokenizer.nextToken();
2051 hfile = token;
2052 }
2053 }
2054 else if (token.equals("-metadata")) {
2055 if(tokenizer.hasMoreTokens()) {
2056 text.append(" ");
2057 text.append(token);
2058 String temp_metadata = tokenizer.nextToken();
2059 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2060 if (replacement != null && !replacement.equals("")) {
2061 token = replacement;
2062 old_metadata = temp_metadata;
2063 new_metadata = replacement;
2064 }
2065 else {
2066 token = temp_metadata;
2067 }
2068 temp_metadata = null;
2069 replacement = null;
2070 }
2071 }
2072 else if (token.equals("-sort")) {
2073 if(tokenizer.hasMoreTokens()) {
2074 text.append(" ");
2075 text.append(token);
2076 String temp_metadata = tokenizer.nextToken();
2077 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2078 if (replacement != null && !replacement.equals("")) {
2079 token = replacement;
2080 }
2081 else {
2082 token = temp_metadata;
2083 }
2084 temp_metadata = null;
2085 replacement = null;
2086 }
2087 }
2088 else if(token.equals("-buttonname")) {
2089 buttonname = token;
2090 }
2091 text.append(' ');
2092 text.append(token);
2093 token = null;
2094 }
2095
2096 // If we replaced the metadata argument and didn't encounter a buttonname, then add one now pointing back to the old metadata name in order to accomodate macro files which required such names (buttonname is metadata name by default)!
2097 if(old_metadata != null && new_metadata != null && buttonname == null) {
2098 text.append(' ');
2099 text.append("-buttonname");
2100 text.append(' ');
2101 text.append(old_metadata);
2102 }
2103 command = text.toString();
2104 // Replace the hfile if we found it
2105 if(hfile != null && new_metadata != null) {
2106 command = command.replaceAll(hfile, new_metadata + ".txt");
2107 }
2108
2109 buttonname = null;
2110 hfile = null;
2111 new_metadata = null;
2112 old_metadata = null;
2113 write(out, command);
2114 } else {
2115 // the rest of the commands just want a string - we read in all the tokens from the tokeniser and get rid of it.
2116 StringBuffer new_command = new StringBuffer(command_type_str);
2117 while (tokenizer.hasMoreTokens()) {
2118 new_command.append(' ');
2119 new_command.append(tokenizer.nextToken());
2120 }
2121
2122 command = new_command.toString();
2123
2124 // There is still one special case, that of the format command. In such a command we have to search for [<target>] to ensure we don't change parts of the format which have nothing to do with the metadata elements.
2125 // we really want to build up the whole command here
2126 boolean format_command = command_type_str.equals("format");
2127 HashMap metadata_mapping = ProfileXMLFileManager.getMetadataMapping();
2128 if (metadata_mapping != null) {
2129 Iterator keys = metadata_mapping.keySet().iterator();
2130 while (keys.hasNext()) {
2131 String target = (String) keys.next();
2132 String replacement = (String) metadata_mapping.get(target);
2133 if (replacement != null && !replacement.equals("")) {
2134 if (format_command) {
2135 target = "\\[" + target + "\\]";
2136 replacement = "{Or}{[" + replacement + "]," + target + "}";
2137 }
2138 command = command.replaceAll(target, replacement);
2139 }
2140 }
2141 }
2142
2143 write(out, command);
2144 }
2145 tokenizer = null;
2146 }
2147 in.close();
2148 in = null;
2149 out.flush();
2150 out.close();
2151 out = null;
2152 }
2153 catch(Exception error) {
2154 DebugStream.printStackTrace(error);
2155 }
2156 // All done, I hope.
2157 }
2158
2159 private void write(BufferedWriter out, String message)
2160 throws Exception {
2161 out.write(message, 0, message.length());
2162 out.newLine();
2163 }
2164
2165
2166 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
2167 private class FMTreeModelListener
2168 implements TreeModelListener {
2169 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2170 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2171 */
2172 public void treeNodesChanged(TreeModelEvent event) {
2173 if(collection != null) {
2174 collection.setSaved(false);
2175 collection.setFilesChanged(true);
2176 }
2177 }
2178 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2179 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2180 */
2181 public void treeNodesInserted(TreeModelEvent event) {
2182 if(collection != null) {
2183 collection.setSaved(false);
2184 collection.setFilesChanged(true);
2185 }
2186 }
2187 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2188 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2189 */
2190 public void treeNodesRemoved(TreeModelEvent event) {
2191 if(collection != null) {
2192 collection.setSaved(false);
2193 collection.setFilesChanged(true);
2194
2195 }
2196 }
2197 /** Any action that changes one of the tree models within a collection, which are the only models we listen to, mean the collections contents have changed and so saved should be set to false.
2198 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2199 */
2200 public void treeStructureChanged(TreeModelEvent event) {
2201 if(collection != null) {
2202 collection.setSaved(false);
2203 }
2204 }
2205 }
2206}
Note: See TracBrowser for help on using the repository browser.