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

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

Again for ticket 152 (moveable collectdir). If the default GS collectdir is the one active in GLI (but all collections are closed) when exiting GLI, this default GS collectdir is not stored in config.xml (in the User Data location). Instead of the last opened collection or last open collectdir, an empty value is written out in the case of the default GS collectdir. This will allow other GS installations to open in their own collectdir. The benefits do not extend to if a collection in another GS' default collectdir was left open. In that case, the collection path is written out to the config.xml file, and the collectdir that a different GLI loads will be the old GLI's collect dir.

  • Property svn:keywords set to Author Date Id Revision
File size: 87.4 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 if (collection == null) {
405 return;
406 }
407 DebugStream.println("Close collection: " + collection.getName());
408
409 // Remove the lock on this file, then remove the collection.
410 File lock_file = new File(getLoadedCollectionDirectoryPath() + LOCK_FILE);
411 lock_file.delete();
412 if (lock_file.exists()) {
413 System.err.println("Warning: Lockfile was not successfully deleted.");
414 }
415
416 // Remove the lock file on the server
417 if (Gatherer.isGsdlRemote) {
418 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), lock_file);
419 }
420
421 // release the current build_log file - else we're unable to delete the last closed collection
422 // until another collection is opened and its opening a new build_log closes this old one
423 Gatherer.g_man.create_pane.options_pane.closeCurrentLogDocument();
424
425 MetadataSetManager.clearMetadataSets();
426 MetadataXMLFileManager.clearMetadataXMLFiles();
427 DocXMLFileManager.clearDocXMLFiles();
428 ProfileXMLFileManager.clearProfileXMLFile();
429
430 collection.destroy();
431 collection = null;
432 collection_tree_model = null;
433 //Configuration.setCollectionConfiguration(null);
434 Gatherer.refresh(Gatherer.COLLECTION_CLOSED);
435 if (Gatherer.g_man != null) {
436 Gatherer.g_man.updateUI(); // !!! Necessary?
437 }
438 }
439
440//This method is no longer used in gs3 since the modification of CollectionConfiguration.java
441// public void convertToGS3Collection() {
442// // Generate the convert_coll_from_gs2.pl command
443// ArrayList command_parts_list = new ArrayList();
444// if ((Utility.isWindows()) && (!Gatherer.isGsdlRemote)) {
445// command_parts_list.add(Configuration.perl_path);
446// command_parts_list.add("-S");
447// }
448// command_parts_list.add(Configuration.getGS3ScriptPath() + "convert_coll_from_gs2.pl");
449// command_parts_list.add("-collectdir");
450// command_parts_list.add(getCollectDirectory());
451// command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
452//
453// // Run the convert_coll_from_gs2.pl command
454// String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
455// GShell process = new GShell(command_parts, GShell.CONVERT, COLLECT, this, null, GShell.GSHELL_CONVERT);
456// process.addGShellListener(this);
457// process.run(); // Don't bother threading this... yet
458//
459// }
460
461 /** When basing a new collection on an existing one, we need to copy
462 * over some extra directories: all except import, archives, building, index
463 * really we just want images, macros, perllib, but there may also be eg style, or other dirs.
464 */
465 private boolean copyExtraBaseCollStuff(File new_coll_dir, File base_coll_dir) {
466 if (!new_coll_dir.isDirectory() || !base_coll_dir.isDirectory()) {
467 return false;
468 }
469 DebugStream.println("Copying extra dirs from the base collection");
470
471
472 File subdirs[] = base_coll_dir.listFiles();
473 for (int i = 0; subdirs != null && i < subdirs.length; i++) {
474 File subdir = subdirs[i];
475 if (subdir.isDirectory()) {
476 String dir_name = subdir.getName();
477 // ignore those we don't need, (archives, buildng, index) and
478 // those we are handling in another place (import, etc, metadata)
479 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")) {
480 continue;
481 }
482 try {
483 // copy the directory
484 File new_coll_subdir = new File(new_coll_dir, dir_name);
485 new_coll_subdir.mkdirs();
486 Gatherer.f_man.getQueue().copyDirectoryContents(subdir, new_coll_subdir);
487 }
488 catch (Exception e) {
489 DebugStream.println("Couldn't copy over the" + subdir+" dir from the base collection: "+e.toString());
490 }
491 }
492 }
493
494 return true;
495 }
496
497 /** 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.
498 * @param description a description of the collection as a String
499 * @param email the email address of the author/maintainer as a String
500 * @param name the short name of the collection, which will subsequently be used to refer to this particular collection, as a String
501 * @param title the longer title of the collection as a String
502 * @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
503 * @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
504 */
505 public void createCollection(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
506 {
507 // Display a modal progress popup to indicate that the collection is being loaded
508 ModalProgressPopup create_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Creating_Collection"), Dictionary.get("CollectionManager.Creating_Collection_Please_Wait"));
509 create_collection_progress_popup.display();
510
511 // Create the collection on a separate thread so the progress bar updates correctly
512 (new CreateCollectionTask(description, email, name, title, base_collection_directory, metadata_sets, create_collection_progress_popup)).start();
513 }
514
515
516 private class CreateCollectionTask
517 extends Thread
518 {
519 private String description = null;
520 private String email = null;
521 private String name = null;
522 private String title = null;
523 private File base_collection_directory = null;
524 private ArrayList metadata_sets = null;
525 private ModalProgressPopup create_collection_progress_popup = null;
526
527 public CreateCollectionTask(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets, ModalProgressPopup create_collection_progress_popup)
528 {
529 this.description = description;
530 this.email = email;
531 this.name = name;
532 this.title = title;
533 this.base_collection_directory = base_collection_directory;
534 this.metadata_sets = metadata_sets;
535 this.create_collection_progress_popup = create_collection_progress_popup;
536 }
537
538 public void run()
539 {
540 createCollectionInternal(description, email, name, title, base_collection_directory, metadata_sets);
541 create_collection_progress_popup.close();
542 }
543 }
544
545
546 private void createCollectionInternal(String description, String email, String name, String title, File base_collection_directory, ArrayList metadata_sets)
547 {
548 try {
549 // first make sure that the collect directory exists
550 File collect_dir = new File(getDefaultCollectDirectory());
551 if (!collect_dir.exists()) {
552 collect_dir.mkdirs();
553 }
554
555 // Create the new collection
556 makeCollection(name, email);
557
558 // Check that the collection has been created successfully
559 String collection_directory_path = getCollectionDirectoryPath(name);
560 if (!new File(collection_directory_path).exists()) {
561 // If there is no collection directory then the creation was unsuccessful, or cancelled
562
563 return;
564 }
565
566 // Check for the existence of the collection configuration file
567 String file_name = ((Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG);
568 File collect_cfg_file = new File(collection_directory_path + "etc" + File.separator + file_name);
569
570 if (!collect_cfg_file.exists()) {
571 System.err.println("Error: no " + file_name + " file has been created!");
572 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);
573 return;
574 }
575
576 // ACTIVE_DIR/log/
577 File log_dir = new File(collection_directory_path + "log");
578 log_dir.mkdirs();
579
580 // Make sure an import folder exists
581 File collection_import_directory = new File(collection_directory_path + "import");
582 if (!collection_import_directory.exists()) {
583 collection_import_directory.mkdirs();
584 if (Gatherer.isGsdlRemote) {
585 Gatherer.remoteGreenstoneServer.newCollectionDirectory(name, collection_import_directory);
586 }
587 }
588
589 // Now create the collection object around the directory.
590 collection = new Collection(new File(collection_directory_path, "gli.col"));
591
592 // for remote case, scheduling causes an Exception on creating a new collection that
593 // can't be recovered from. For GS3, it doesn't work since it it trying to access etc/main.cfg
594 if (canDoScheduling()) {
595 scheduling();
596 }
597
598 MetadataSetManager.clearMetadataSets();
599 MetadataXMLFileManager.clearMetadataXMLFiles();
600 DocXMLFileManager.clearDocXMLFiles();
601
602 // Import default metadata sets, if any
603 // for (int i = 0; metadata_sets != null && i < metadata_sets.size(); i++) {
604 // importMetadataSet((MetadataSet) metadata_sets.get(i));
605 // }
606
607 ProfileXMLFileManager.loadProfileXMLFile(new File(collection_directory_path + "metadata"));
608
609 // Before creating the CollectionDesignManager check if we are basing it upon some other collection
610 if (base_collection_directory != null) {
611 DebugStream.println("Basing new collection on existing one: " + base_collection_directory);
612
613 // If we're using a remote Greenstone server, download the collection shell to get the files needed
614 if (Gatherer.isGsdlRemote) {
615 String base_collection_name = base_collection_directory.getName();
616 Gatherer.remoteGreenstoneServer.downloadCollection(base_collection_name);
617 }
618
619 collection.setBaseCollection(base_collection_directory.getAbsolutePath());
620 // copy over other needed directories
621 copyExtraBaseCollStuff(new File(collection_directory_path), base_collection_directory);
622
623 // Try to import any existing metadata sets for this collection
624 // Look in base_collection_directory/metadata and import any metadata sets found.
625 File base_metadata_directory = new File(base_collection_directory, "metadata");
626 ArrayList base_metadata_sets = MetadataSetManager.listMetadataSets(base_metadata_directory);
627 if (base_metadata_sets != null) {
628 for (int i = 0; i < base_metadata_sets.size(); i++) {
629 importMetadataSet((MetadataSet) base_metadata_sets.get(i));
630 }
631 }
632 else {
633 DebugStream.println("This base collection has no metadata directory.");
634 }
635
636 // Now we update our collect.cfg
637 DebugStream.println("Copy and update " + file_name + " from base collection.");
638
639 if (Gatherer.GS3 == true) {
640 updateCollectionConfigXML(new File(base_collection_directory, Utility.CONFIG_GS3_FILE),
641 new File(collection_directory_path, Utility.CONFIG_GS3_FILE));
642 } else {
643 updateCollectionCFG(new File(base_collection_directory, Utility.CONFIG_FILE),
644 new File(collection_directory_path, Utility.CONFIG_FILE),
645 description, email, title);
646 }
647 }
648 else {
649 // only load metadata sets here if we have not based the collection on any other.
650 // Load the default metadata sets
651 addDefaultMetadataSets();
652
653 // Make sure we always have the extracted metadata set
654 addRequiredMetadataSets();
655 }
656
657 collection.cdm = new CollectionDesignManager(new File(getLoadedCollectionCfgFilePath()));
658
659 // We always set title and description here rather than calling mkcol.pl with Unicode arguments
660 CollectionMeta collection_name_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONNAME_STR);
661 collection_name_collectionmeta.setValue(title);
662 CollectionMeta collection_extra_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_COLLECTIONEXTRA_STR);
663 collection_extra_collectionmeta.setValue(description);
664
665 // 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.
666 if (base_collection_directory != null) {
667 // Update the creator and maintainer
668 CollectionMeta creator_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getCreator());
669 creator_collectionmeta.setValue(email);
670 creator_collectionmeta = null;
671 CollectionMeta maintainer_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getMaintainer());
672 maintainer_collectionmeta.setValue(email);
673 maintainer_collectionmeta = null;
674
675 // All collections based on others are automatically public
676 CollectionMeta public_collectionmeta = new CollectionMeta(collection.cdm.collect_config.getPublic());
677 public_collectionmeta.setValue(StaticStrings.TRUE_STR);
678 public_collectionmeta = null;
679
680 // Finally reset the icons
681 CollectionMeta icon_collection_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTION_STR);
682 icon_collection_collectionmeta.setValue(StaticStrings.EMPTY_STR);
683 icon_collection_collectionmeta = null;
684 CollectionMeta icon_collection_small_collectionmeta = collection.cdm.collectionmeta_manager.getMetadatum(StaticStrings.COLLECTIONMETADATA_ICONCOLLECTIONSMALL_STR);
685 icon_collection_small_collectionmeta.setValue(StaticStrings.EMPTY_STR);
686 icon_collection_small_collectionmeta = null;
687 }
688
689 saveCollection();
690
691 // Create a lock file
692 createLockFile(new File(collection_directory_path, LOCK_FILE));
693
694 // We're done. Let everyone know.
695 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
696 }
697 catch (Exception error) {
698 DebugStream.printStackTrace(error);
699 }
700 }
701
702 private void scheduling()
703 throws Exception
704 {
705 //try to obtain email address of collection owner if it exists...
706 String stmp = Configuration.getEmail();
707 if(stmp != null) {
708 collection.schedule_options.setValue("toaddr", false, Configuration.getEmail());
709 }
710
711 //The next few items deal with updating the SMTP server, and the to: and from: addresses
712 //from main.cfg and the collection configuration. if no changes are made, or the
713 //values are result to NULL, any existing values are kept.
714
715 //try to obtain email address of Greenstone installation webmaster for - used to indicate "sender".
716 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
717 BufferedReader maincfg = new BufferedReader(new FileReader(mcfg));
718 stmp = "";
719 String fromaddr = "";
720 while((stmp = maincfg.readLine()) != null) {
721 if(stmp.startsWith("maintainer")) {
722 fromaddr = stmp.substring(10); //length of MailServer
723 fromaddr = fromaddr.trim();
724 break;
725 }
726 }
727 maincfg.close();
728 if(!fromaddr.equals("NULL") && !fromaddr.equals("null")) {
729 collection.schedule_options.setValue("fromaddr", false, fromaddr);
730 }
731
732 //try to obtain an smtp server address from main.cfg. If that fails,
733 //try mail.server if an email address exists. If that fails,
734 //maybe a message to set attribute in main.cfg?
735 //i'm pretty sure there exists functionality to do this, but
736 //i'll finish this faster if I just wrote it
737
738
739 maincfg = new BufferedReader(new FileReader(mcfg));
740 String smtptmp = "NULL";
741 while((stmp = maincfg.readLine()) != null) {
742 if(stmp.startsWith("MailServer")) {
743 smtptmp = stmp.substring(10); //length of MailServer
744 smtptmp = smtptmp.trim();
745 break;
746 }
747 }
748 maincfg.close();
749
750 //try if lookup fails
751 if(smtptmp.equals("NULL") || smtptmp.equals("null")) {
752 String email2=fromaddr;
753 if(!email2.equals("NULL") && !email2.equals("null")) {
754 int loc = email2.indexOf('@');
755 email2 = email2.substring(loc+1);
756 smtptmp = "mail."+email2;
757 }
758 }
759 if(!smtptmp.equals("NULL") && !smtptmp.equals("null")) {
760 collection.schedule_options.setValue("smtp", false, smtptmp);
761 }
762
763 }
764
765
766 private void createLockFile(File lock_file)
767 {
768 try {
769 Document default_lockfile = XMLTools.parseXMLFile("xml/" + LOCK_FILE, true);
770 String user_name = System.getProperty("user.name");
771 Element person_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "User");
772 person_element.appendChild(default_lockfile.createTextNode(user_name));
773 person_element = null;
774 user_name = null;
775 String machine_name = Utility.getMachineName();
776 Element machine_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Machine");
777 machine_element.appendChild(default_lockfile.createTextNode(machine_name));
778 machine_element = null;
779 machine_name = null;
780 String date_time = Utility.getDateString();
781 Element date_element = (Element) XMLTools.getNodeFromNamed(default_lockfile.getDocumentElement(), "Date");
782 date_element.appendChild(default_lockfile.createTextNode(date_time));
783 date_element = null;
784 date_time = null;
785 XMLTools.writeXMLFile(lock_file, default_lockfile);
786 }
787 catch (Exception exception) {
788 DebugStream.printStackTrace(exception);
789 }
790 }
791
792
793 public boolean deleteCollection(String collection_name)
794 {
795 // First we must release the collection from the local library, if it's running
796 if (LocalLibraryServer.isRunning() == true) {
797 LocalLibraryServer.releaseCollection(collection_name);
798 }
799
800 // Delete the collection on the server if we're using a remote Greenstone
801 if (Gatherer.isGsdlRemote) {
802 Gatherer.remoteGreenstoneServer.deleteCollection(collection_name);
803 }
804
805 // if Greenstone3, need to deactivate the collection on the server
806 if (Gatherer.GS3) {
807 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection_name);
808 }
809
810 // Now delete the collection directory
811 return Utility.delete(new File(getCollectionDirectoryPath(collection_name)));
812 }
813
814
815 public void fireFileAddedToCollection(File file)
816 {
817 // Send the event off to all the CollectionContentsChangedListeners
818 for (int i = 0; i < collection_contents_changed_listeners.size(); i++) {
819 ((CollectionContentsChangedListener) collection_contents_changed_listeners.get(i)).fileAddedToCollection(file);
820 }
821 }
822
823
824 /** Retrieve the current collection.
825 * @return The <strong>Collection</strong> itself.
826 */
827 public Collection getCollection() {
828 return collection;
829 }
830
831
832 /** Returns the absolute filename of the specified collection's directory.
833 */
834 static public String getCollectionDirectoryPath(String collection_name)
835 {
836 return Gatherer.getCollectDirectoryPath() + collection_name + File.separator;
837 }
838
839
840 /** Returns the absolute filename of the loaded collection's archives directory.
841 */
842 static public String getLoadedCollectionArchivesDirectoryPath()
843 {
844 return getLoadedCollectionDirectoryPath() + "archives" + File.separator;
845 }
846
847 /** Returns the absolute filename of the loaded collection's export directory.
848 */
849 static public String getLoadedCollectionExportDirectoryPath()
850 {
851 return getLoadedCollectionDirectoryPath() + "export" + File.separator;
852 }
853
854
855
856 /** Returns the absolute filename of the loaded collection's building directory.
857 */
858 static public String getLoadedCollectionBuildingDirectoryPath()
859 {
860 return getLoadedCollectionDirectoryPath() + "building" + File.separator;
861 }
862
863
864 /** Returns the absolute filename of the loaded collection's collect.cfg file.
865 */
866 static public String getLoadedCollectionCfgFilePath()
867 {
868 String path = (Gatherer.GS3 == true)? Utility.COLLECTION_CONFIG_XML : Utility.COLLECT_CFG;
869 return getLoadedCollectionEtcDirectoryPath() + path;
870 }
871
872
873 /** Returns the absolute filename of the loaded collection's directory.
874 */
875 static public String getLoadedCollectionDirectoryPath()
876 {
877 return collection.getCollectionDirectory().getPath() + File.separator;
878 }
879
880
881 /** Returns the absolute filename of the loaded collection's etc directory.
882 */
883 static public String getLoadedCollectionEtcDirectoryPath()
884 {
885 return getLoadedCollectionDirectoryPath() + "etc" + File.separator;
886 }
887
888
889 /** Returns the absolute filename of the loaded collection's .col file.
890 */
891 static public String getLoadedCollectionColFilePath()
892 {
893 return getLoadedCollectionDirectoryPath() + "gli.col";
894 }
895
896
897 /** Returns the absolute filename of the loaded collection's images directory.
898 */
899 static public String getLoadedCollectionImagesDirectoryPath()
900 {
901 return getLoadedCollectionDirectoryPath() + "images" + File.separator;
902 }
903
904
905 /** Returns the absolute filename of the loaded collection's import directory.
906 */
907 static public String getLoadedCollectionImportDirectoryPath()
908 {
909 return getLoadedCollectionDirectoryPath() + "import" + File.separator;
910 }
911
912
913 /** Returns the absolute filename of the loaded collection's index directory.
914 */
915 static public String getLoadedCollectionIndexDirectoryPath()
916 {
917 return getLoadedCollectionDirectoryPath() + "index" + File.separator;
918 }
919
920
921 /** Returns the absolute filename of the loaded collection's log directory.
922 */
923 static public String getLoadedCollectionLogDirectoryPath()
924 {
925 return getLoadedCollectionDirectoryPath() + "log" + File.separator;
926 }
927
928
929 /** Returns the absolute filename of the loaded collection's macros directory.
930 */
931 static public String getLoadedCollectionMacrosDirectoryPath()
932 {
933 return getLoadedCollectionDirectoryPath() + "macros" + File.separator;
934 }
935
936
937 /** Returns the absolute filename of the loaded collection's metadata directory.
938 */
939 static public String getLoadedCollectionMetadataDirectoryPath()
940 {
941 return getLoadedCollectionDirectoryPath() + "metadata" + File.separator;
942 }
943
944
945 /** Returns the (group-qualified) name of the loaded collection with
946 * OS-dependent file separator.
947 */
948 static public String getLoadedCollectionName()
949 {
950 return CollectionManager.getLoadedCollectionName(false);
951 }
952
953 /** Returns the (group-qualified) name of the loaded collection with
954 * OS-dependent space separator.
955 * @url true if url-type forward slashes, false if OS-dependent filesystem slashes.
956 */
957 static public String getLoadedCollectionName(boolean url)
958 {
959 if (collection != null) {
960 //return collection.getName();
961 return collection.getGroupQualifiedName(url);
962 }
963
964 return null;
965 }
966
967 /** @return the subname of any collection (stripped of any collection-group). */
968 static public String getLoadedCollectionTailName()
969 {
970 if (collection != null) {
971 return collection.getCollectionTailName();
972 }
973
974 return null;
975 }
976
977 /** Returns the "collectionGroupName/collectionName" or just the collectionName
978 * depending on whether the collection is part of a collection group or not.
979 * If url = true, then returns the sub-path as a URL (containing / only),
980 * and if url = false, then the sub-path is returned in filepath form
981 * (\ or /, depending on the OS).
982 */
983 static public String getLoadedGroupQualifiedCollectionName(boolean url)
984 {
985 if (collection != null) {
986 return collection.getGroupQualifiedName(url);
987 }
988
989 return null;
990 }
991
992 public CollectionTree getCollectionTree()
993 {
994 if (collection_tree == null) {
995 collection_tree = new CollectionTree(collection_tree_model, true);
996 }
997
998 return collection_tree;
999 }
1000
1001
1002 /** Retrieve the tree model associated with the current collection. */
1003 public CollectionTreeModel getCollectionTreeModel()
1004 {
1005 if (collection_tree_model == null && collection != null) {
1006 // Use the import directory to generate a new CollectionTreeModel
1007 collection_tree_model = new CollectionTreeModel(new CollectionTreeNode(new File(getLoadedCollectionImportDirectoryPath())));
1008 // Ensure that the manager is a change listener for the tree.
1009 if (fm_tree_model_listener == null) {
1010 fm_tree_model_listener = new FMTreeModelListener();
1011 }
1012 collection_tree_model.addTreeModelListener(fm_tree_model_listener);
1013 }
1014 return collection_tree_model;
1015 }
1016
1017
1018 /** This method when called, creates a new GShell in order to run the import.pl script.
1019 * @see org.greenstone.gatherer.Configuration
1020 * @see org.greenstone.gatherer.Gatherer
1021 * @see org.greenstone.gatherer.gui.BuildOptions
1022 * @see org.greenstone.gatherer.shell.GShell
1023 * @see org.greenstone.gatherer.shell.GShellListener
1024 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
1025 * @see org.greenstone.gatherer.util.Utility
1026 */
1027 public void importCollection() {
1028 importing = true;
1029
1030 if (!saved()) {
1031 DebugStream.println("CollectionManager.importCollection().forcesave");
1032 import_monitor.saving();
1033 saveCollection();
1034 }
1035
1036 DebugStream.println("CollectionManager.importCollection()");
1037 DebugStream.println("Is event dispatch thread: " + SwingUtilities.isEventDispatchThread());
1038 //check that we can remove the old index before starting import
1039 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
1040 if (index_dir.exists()) {
1041 DebugStream.println("Old Index = " + index_dir.getAbsolutePath()+", testing for deletability");
1042 if (!canDelete(index_dir)) {
1043 // tell the user
1044 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Delete_Index"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1045 // tell the gui manager
1046 // a message for the building log
1047 GShellEvent event = new GShellEvent(this, 0, GShell.IMPORT, Dictionary.get("CollectionManager.Cannot_Delete_Index_Log"), GShell.ERROR);
1048 Gatherer.g_man.create_pane.message(event);
1049 event = new GShellEvent(this, 0, GShell.IMPORT, "", GShell.ERROR);
1050 Gatherer.g_man.create_pane.processComplete(event);
1051 importing = false;
1052 return;
1053 }
1054 }
1055
1056 // Generate the import.pl command
1057 ArrayList command_parts_list = new ArrayList();
1058 if (!Gatherer.isGsdlRemote) {
1059 command_parts_list.add(Configuration.perl_path);
1060 command_parts_list.add("-S");
1061 }
1062
1063 if (Configuration.fedora_info.isActive()) {
1064 command_parts_list.add(scriptPath + "g2f-import.pl");
1065
1066 command_parts_list.add("-hostname");
1067 command_parts_list.add(Configuration.fedora_info.getHostname());
1068
1069 command_parts_list.add("-port");
1070 command_parts_list.add(Configuration.fedora_info.getPort());
1071
1072 command_parts_list.add("-username");
1073 command_parts_list.add(Configuration.fedora_info.getUsername());
1074
1075 command_parts_list.add("-password");
1076 command_parts_list.add(Configuration.fedora_info.getPassword());
1077
1078 command_parts_list.add("-protocol");
1079 command_parts_list.add(Configuration.fedora_info.getProtocol());
1080 }
1081 else {
1082 String cmdPrefix = null;
1083 if ( CollectionDesignManager.isCompleteBuild() ) {
1084 cmdPrefix = "full-";
1085 CollectionDesignManager.setImportWasFull( true );
1086 } else {
1087 cmdPrefix = "incremental-";
1088 CollectionDesignManager.setImportWasFull( false );
1089 }
1090 command_parts_list.add(scriptPath + cmdPrefix + "import.pl"); // scriptPath already set according to local or remote case
1091 }
1092
1093 command_parts_list.add("-gli");
1094 command_parts_list.add("-language");
1095 command_parts_list.add(Configuration.getLanguage());
1096 if(!Gatherer.isGsdlRemote) {
1097 command_parts_list.add("-collectdir");
1098 command_parts_list.add(getCollectDirectory());
1099 }
1100
1101 String[] import_options = collection.import_options.getValues();
1102 for (int i = 0; i < import_options.length; i++) {
1103 System.err.println( "Tacking on option: " + import_options[i] );
1104 command_parts_list.add(import_options[i]);
1105 }
1106
1107 command_parts_list.add(collection.getGroupQualifiedName(false)); // (colgroup/)colname
1108
1109 // Run the import.pl command
1110 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1111 GShell shell = new GShell(command_parts, GShell.IMPORT, BUILDING, this, import_monitor, GShell.GSHELL_IMPORT);
1112 //shell.setEventProperty("is_incremental", Boolean.toString(is_incremental));
1113 shell.addGShellListener(Gatherer.g_man.create_pane);
1114 shell.addGShellListener(Gatherer.g_man.format_pane);
1115 shell.start();
1116 DebugStream.println("CollectionManager.importCollection().return");
1117
1118 importing = false;
1119 }
1120
1121
1122 public void importMetadataSet(MetadataSet external_metadata_set)
1123 {
1124 // Copy the .mds file into the collection's "metadata" folder...
1125 File external_metadata_set_file = external_metadata_set.getMetadataSetFile();
1126
1127 // ...but not if it is the redundant "hidden.mds" file
1128 if (external_metadata_set_file.getName().equals("hidden.mds")) {
1129 return;
1130 }
1131
1132 // ...and only if it doesn't already exist
1133 File metadata_set_file = new File(getLoadedCollectionMetadataDirectoryPath(), external_metadata_set_file.getName());
1134 if (!metadata_set_file.exists()) {
1135 try {
1136 Gatherer.f_man.getQueue().copyFile(external_metadata_set_file, metadata_set_file, false);
1137
1138 // If we're using a remote Greenstone server, upload the metadata file
1139 if (Gatherer.isGsdlRemote) {
1140 Gatherer.remoteGreenstoneServer.uploadCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1141 }
1142 }
1143 catch (Exception exception) {
1144 DebugStream.printStackTrace(exception);
1145 }
1146
1147 // Load it into the MetadataSetManager
1148 MetadataSetManager.loadMetadataSet(metadata_set_file);
1149 }
1150 }
1151
1152
1153 /** 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.
1154 * @return true if the gli is currently importing
1155 */
1156 public boolean isImporting() {
1157 return importing;
1158 }
1159
1160
1161 public void loadCollection(String collection_file_path)
1162 {
1163 // Display a modal progress popup to indicate that the collection is being loaded
1164 ModalProgressPopup load_collection_progress_popup = new ModalProgressPopup(Dictionary.get("CollectionManager.Loading_Collection"), Dictionary.get("CollectionManager.Loading_Collection_Please_Wait"));
1165 load_collection_progress_popup.display();
1166
1167 // Load the collection on a separate thread so the progress bar updates correctly
1168 (new LoadCollectionTask(collection_file_path, load_collection_progress_popup)).start();
1169 }
1170
1171
1172 private class LoadCollectionTask
1173 extends Thread
1174 {
1175 private String collection_file_path = null;
1176 private ModalProgressPopup load_collection_progress_popup = null;
1177
1178 public LoadCollectionTask(String collection_file_path, ModalProgressPopup load_collection_progress_popup)
1179 {
1180 this.collection_file_path = collection_file_path;
1181 this.load_collection_progress_popup = load_collection_progress_popup;
1182 }
1183
1184 public void run()
1185 {
1186 loadCollectionInternal(collection_file_path);
1187 load_collection_progress_popup.close();
1188 Gatherer.setMenuBarEnabled(true);
1189 }
1190 }
1191
1192
1193 /** Attempts to load the given collection. Currently uses simple serialization of the collection class.
1194 * @param location The path to the collection as a <strong>String</strong>.
1195 * @see org.greenstone.gatherer.Configuration
1196 * @see org.greenstone.gatherer.Gatherer
1197 * @see org.greenstone.gatherer.collection.Collection
1198 * @see org.greenstone.gatherer.util.Utility
1199 */
1200 private void loadCollectionInternal(String location)
1201 {
1202 DebugStream.println("Loading collection " + location + "...");
1203
1204
1205 // Check we have actually been given a .col file.
1206 if (!location.endsWith(".col")) {
1207 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Not_Col_File", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1208 DebugStream.println("CollectionManager.loadCollection: Haven't been given a .col file.");
1209 return;
1210 }
1211
1212 // Check that the collection configuration file is available
1213 File collection_file = new File(location);
1214
1215 //String collection_name = collection_directory.getName();
1216 String collection_name = "";
1217 File collection_directory = collection_file.getParentFile();
1218
1219 // To get colname = (colgroup/)coltailname, subtract Gatherer.getCollectDirectoryPath() from collection_directory:
1220 int index = collection_directory.getAbsolutePath().indexOf(Gatherer.getCollectDirectoryPath());
1221 if(index == -1) {
1222 System.err.println("*** ERROR: collection directory " + collection_directory + " is not located in collect folder: " + Gatherer.getCollectDirectoryPath());
1223 } else {
1224 index += Gatherer.getCollectDirectoryPath().length();
1225 collection_name = collection_directory.getAbsolutePath().substring(index);
1226 }
1227
1228 if (Gatherer.isGsdlRemote) {
1229 if (Gatherer.remoteGreenstoneServer.downloadCollection(collection_name).equals("")) {
1230 return;
1231 }
1232 }
1233
1234 // Ensure that the collection directory exists
1235 if (collection_directory == null || !collection_directory.exists()) {
1236 // We can't open this
1237 System.err.println("CollectionManager.loadCollection: No collection directory.");
1238 return;
1239 }
1240
1241 String file_str = (Gatherer.GS3)? Utility.CONFIG_GS3_FILE : Utility.CONFIG_FILE;
1242 File collection_config_file = new File(collection_directory, file_str);
1243 if (!collection_config_file.exists()) {
1244 System.err.println("CollectionManager.loadCollection: No config file.");
1245 collection_directory = null;
1246 collection_config_file = null;
1247 return;
1248 }
1249
1250 // Ensure that an import directory exists for this collection
1251 File collection_import_directory = new File(collection_directory, "import");
1252 if (!collection_import_directory.exists()) {
1253 collection_import_directory.mkdir();
1254 }
1255
1256 // Special case of a user trying to open an old greenstone collection.
1257 boolean non_gli_collection = false;
1258 File collection_metadata_directory = new File(collection_directory, "metadata");
1259 if (!collection_metadata_directory.exists()) {
1260 DebugStream.println("Loading non-gatherer collection...");
1261 // Show a warning message in case user wants to quit now
1262 non_gli_collection = true;
1263 WarningDialog legacy_dialog = new WarningDialog("warning.LegacyCollection", Dictionary.get("LegacyCollection.Title"), Dictionary.get("LegacyCollection.Message"), null, true);
1264 if (legacy_dialog.display()==JOptionPane.CANCEL_OPTION) {
1265 legacy_dialog.dispose();
1266 collection_directory = null;
1267 collection_config_file = null;
1268 return;
1269 }
1270 legacy_dialog.dispose();
1271
1272 }
1273
1274 // Now determine if a lock already exists on this collection.
1275 File lock_file = new File(collection_file.getParentFile(), LOCK_FILE);
1276 if (lock_file.exists()) {
1277 LockFileDialog dialog = new LockFileDialog(Gatherer.g_man, collection_name, lock_file);
1278 int choice = dialog.getChoice();
1279 dialog.dispose();
1280 dialog = null;
1281
1282 if (choice != LockFileDialog.YES_OPTION) {
1283 // user has cancelled
1284 lock_file = null;
1285 collection_directory = null;
1286 collection_config_file = null;
1287 return;
1288 }
1289
1290 lock_file.delete();
1291 }
1292
1293 // now we are using gli.col - old colls may have used the collection name
1294 if (!collection_file.exists()) {
1295 File old_coll_file = new File(collection_directory, collection_name+".col");
1296 if (old_coll_file.exists()) {
1297 try {
1298 old_coll_file.renameTo(collection_file);
1299 } catch (Exception e) {
1300 DebugStream.println("Couldn't rename "+old_coll_file.getName()+" to gli.col. Will just carry on with default gli.col");
1301 // but just carry on.
1302 }
1303 }
1304 }
1305
1306 try {
1307 // Create a lock file.
1308 createLockFile(lock_file);
1309 // This lock file may not have been created so check
1310 if(!lock_file.canWrite()) {
1311 // The lock file cannot be written to. Most likely cause incorrect file permissions.
1312 System.err.println("Cannot write lock file!");
1313 String args[] = new String[2];
1314 args[0] = location;
1315 args[1] = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{lock_file.getAbsolutePath()});
1316 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS")!=-1){
1317 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1318 args[1] += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1319 //}
1320 }
1321 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1322 args = null;
1323 return;
1324 }
1325
1326 // need to fix this up as currently it craps out if the .col file is not there, which is may not always be.
1327 if (canDoScheduling() && collection_file.exists()) {
1328 //THIS LOOKS LIKE THE BEST PLACE TO TRY AND UPDATE .col FILES FOR EXISTING COLLECTIONS...Wendy
1329 // Don't need to update anything if collection_file doesn't exist yet.
1330 //First, see if "Schedule" exists in the XMl File...
1331 BufferedReader bir = new BufferedReader(new FileReader(collection_file));
1332 boolean flag = false;
1333 try {
1334 String stmp = new String();
1335
1336 while((stmp = bir.readLine()) != null) {
1337 stmp = stmp.trim();
1338 if(stmp.equals("<Schedule>") || stmp.equals("<Schedule/>")) {
1339 flag = true;
1340 break;
1341 }
1342 }
1343 bir.close();
1344
1345 } catch (IOException ioe) {
1346 DebugStream.printStackTrace(ioe);
1347 }
1348
1349 //modify if old .col (i.e. no Schedule exists in XML file)
1350 if(!flag) {
1351 File new_collection_file = new File(collection_directory.getAbsolutePath() + "/tmp.col");
1352
1353
1354 BufferedWriter bor = new BufferedWriter(new FileWriter(new_collection_file));
1355 bir = new BufferedReader(new FileReader(collection_file));
1356
1357 try {
1358 String stmp = new String();
1359 while((stmp = bir.readLine()) != null) {
1360 String stmp2 = stmp.trim();
1361 if(stmp2.startsWith("<!ELEMENT Argument")) {
1362 bor.write(" <!ELEMENT Schedule (Arguments*)>\n");
1363 }
1364 else if(stmp2.equals("</BuildConfig>")) {
1365 bor.write(" <Schedule/>\n");
1366 }
1367
1368 bor.write(stmp + "\n");
1369
1370 }
1371 bir.close();
1372 bor.close();
1373 } catch (IOException ioe) {
1374 DebugStream.printStackTrace(ioe);
1375 }
1376
1377 //copy over tmp.col to replace
1378 try {
1379 collection_file.delete();
1380 new_collection_file.renameTo(collection_file);
1381 } catch (Exception e) {
1382 DebugStream.printStackTrace(e);
1383 }
1384 }
1385 }
1386
1387 // Open the collection file
1388 this.collection = new Collection(collection_file);
1389 if (collection.error) {
1390 collection = null;
1391 // Remove lock file
1392 if (lock_file.exists()) {
1393 lock_file.delete();
1394 }
1395 throw(new Exception(Dictionary.get("CollectionManager.Missing_Config"))); // this error message does not agree with the error
1396 }
1397
1398 if (canDoScheduling()) {
1399 scheduling();
1400 }
1401
1402 // These may have been set in the past, but are no longer used
1403 // by GLI
1404 collection.import_options.removeValue("removeold");
1405 collection.import_options.removeValue("keepold");
1406
1407 MetadataSetManager.clearMetadataSets();
1408 MetadataSetManager.loadMetadataSets(collection_metadata_directory);
1409
1410 // Make sure we always have the extracted metadata set
1411 addRequiredMetadataSets();
1412
1413 ProfileXMLFileManager.loadProfileXMLFile(collection_metadata_directory);
1414
1415 // If this is a non-GLI (legacy) collection, load the default metadata sets
1416 if (non_gli_collection) {
1417 addDefaultMetadataSets();
1418
1419 // Recurse the import folder tree, backing up the metadata.xml files before they are edited
1420 LegacyCollectionImporter.backupMetadataXMLFiles(collection_directory);
1421 }
1422
1423 // Read through the metadata.xml files in the import directory, building up the metadata value trees
1424 MetadataXMLFileManager.clearMetadataXMLFiles();
1425 MetadataXMLFileManager.loadMetadataXMLFiles(collection_import_directory,collection.toSkimFile());
1426
1427
1428 // get rid of the previous scan through docxml files
1429 DocXMLFileManager.clearDocXMLFiles();
1430
1431 if (Configuration.fedora_info.isActive()) { // FLI case
1432 // Read through the docmets.xml files in the export directory
1433 File collection_export_directory = new File(getLoadedCollectionExportDirectoryPath());
1434 DocXMLFileManager.loadDocXMLFiles(collection_export_directory,"docmets.xml");
1435 }
1436 else {
1437 // Read through the doc.xml files in the archives directory
1438 File collection_archives_directory = new File(getLoadedCollectionArchivesDirectoryPath());
1439 DocXMLFileManager.loadDocXMLFiles(collection_archives_directory,"doc.xml");
1440 }
1441
1442
1443 // Get a list of the collection specific classifiers and plugins
1444 Classifiers.loadClassifiersList(collection_name);
1445 Plugins.loadPluginsList(collection_name);
1446
1447 collection.cdm = new CollectionDesignManager(collection_config_file);
1448 if (non_gli_collection) {
1449 // Change the classifiers to use the namespaced element names
1450 LegacyCollectionImporter.updateClassifiers(collection.cdm);
1451 }
1452
1453 // We're done. Let everyone know.
1454 DebugStream.println(Dictionary.get("CollectionManager.Loading_Successful", collection_name));
1455 Gatherer.refresh(Gatherer.COLLECTION_OPENED);
1456 }
1457 catch (Exception error) {
1458 // There is obviously no existing collection present.
1459 DebugStream.printStackTrace(error);
1460 error.printStackTrace();
1461 if(error.getMessage() != null) {
1462 String[] args = new String[2];
1463 args[0] = location;
1464 args[1] = error.getMessage();
1465 //args[1] = "The Librarian Interface does not have permission to write to... Please check file permissions.";
1466 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open_With_Reason", args), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1467 }
1468 else {
1469 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Cannot_Open", location), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1470 }
1471 }
1472
1473 lock_file = null;
1474 collection_directory = null;
1475 collection_config_file = null;
1476 }
1477
1478 /** At present, scheduling only works for GS2, only when GS2 is local and only when GLI runs from
1479 * within a GS2 installation. This method can be adjusted as scheduling becomes available for more
1480 * more situations. */
1481 public static boolean canDoScheduling() {
1482 // Would be nice to support more of these, rather than returning false
1483 if(Gatherer.isGsdlRemote) {
1484 return false;
1485 }
1486 if(Gatherer.GS3) {
1487 return false;
1488 }
1489 if (Configuration.fedora_info.isActive()) {
1490 return false;
1491 }
1492
1493 // GS2's etc/main.cfg is necessary for scheduling, but scheduling looks for it locally:
1494 // it assumes GLI is inside a GS2 installation
1495 File mcfg = new File(LocalGreenstone.getDirectoryPath() + File.separator + "etc" + File.separator + "main.cfg");
1496 if(!mcfg.exists()) {
1497 System.out.println("Cannot do scheduling, since there is no file: " + mcfg.getAbsolutePath()
1498 + ".\nScheduling presently depends on GLI running from inside a GS2.");
1499 return false;
1500 }
1501
1502 return true;
1503 }
1504
1505 private void makeCollection(String name, String email)
1506 {
1507 // Generate the mkcol.pl command
1508 ArrayList command_parts_list = new ArrayList();
1509 if (!Gatherer.isGsdlRemote) {
1510 command_parts_list.add(Configuration.perl_path);
1511 command_parts_list.add("-S");
1512 }
1513 command_parts_list.add(scriptPath + "mkcol.pl");
1514 if(Gatherer.GS3) {
1515 command_parts_list.add(Utility.GS3MODE_ARGUMENT); // add '-gs3mode'
1516 }
1517 if(!Gatherer.isGsdlRemote) {
1518 command_parts_list.add("-collectdir");
1519 command_parts_list.add(getDefaultCollectDirectory());
1520 }
1521 command_parts_list.add("-win31compat");
1522 command_parts_list.add((Gatherer.isGsdlRemote) ? "false" : "true");
1523
1524 if (email != null && !email.equals("")) {
1525 command_parts_list.add("-creator");
1526 command_parts_list.add(email);
1527 }
1528
1529 command_parts_list.add(name);
1530
1531 // Run the mkcol.pl command
1532 String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
1533 //for(int i = 0; i < command_parts.length; i++) {
1534 ///ystem.err.println("\""+command_parts[i]+"\"");
1535 //}
1536
1537 GShell process = new GShell(command_parts, GShell.NEW, COLLECT, this, null, GShell.GSHELL_NEW);
1538 process.run(); // Don't bother threading this... yet
1539 }
1540
1541
1542 /** 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.
1543 * @param event A <strong>GShellEvent</strong> which contains a the message.
1544 */
1545 public synchronized void message(GShellEvent event) {
1546
1547 }
1548
1549
1550 public void metadataChanged(CollectionTreeNode[] file_nodes)
1551 {
1552 if (collection != null) {
1553 collection.setMetadataChanged(true);
1554 }
1555 }
1556
1557
1558 public void openCollectionFromLastTime() {
1559 // If there was an open collection last session, reopen it.
1560 // Skip if there was none or only a collect folder instead of any previously-opened collection
1561 if (Gatherer.open_collection_file_path != null
1562 && !(new File(Gatherer.open_collection_file_path).isDirectory())) {
1563 // Load the collection now
1564 loadCollection(Gatherer.open_collection_file_path);
1565 }
1566 }
1567
1568
1569 /** This call is fired whenever a process within a GShell created by this class begins.
1570 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1571 * @see org.greenstone.gatherer.Gatherer
1572 * @see org.greenstone.gatherer.gui.GUIManager
1573 * @see org.greenstone.gatherer.shell.GShell
1574 */
1575 public synchronized void processBegun(GShellEvent event) {
1576 DebugStream.println("CollectionManager.processBegun(" + event.getType() + ")");
1577 ///ystem.err.println("ProcessBegun " + event.getType());
1578 // If this is one of the types where we wish to lock user control
1579 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), true);
1580 }
1581 /** This call is fired whenever a process within a GShell created by this class ends.
1582 * @param event A <strong>GShellEvent</strong> containing information about the GShell process.
1583 * @see org.greenstone.gatherer.Gatherer
1584 * @see org.greenstone.gatherer.gui.GUIManager
1585 * @see org.greenstone.gatherer.shell.GShell
1586 */
1587 public synchronized void processComplete(GShellEvent event) {
1588 //ystem.err.println("CollectionManager.processComplete(" + event.getType() + ")");
1589 Gatherer.g_man.lockCollection((event.getType() == GShell.IMPORT), false);
1590 ///ystem.err.println("Received process complete event - " + event);
1591 // If we were running an import, now run a build.
1592 if(event.getType() == GShell.IMPORT && event.getStatus() == GShell.OK) {
1593 // Finish import.
1594 collection.setImported(true);
1595 collection.setFilesChanged(false);
1596 collection.setMetadataChanged(false);
1597 buildCollection();
1598 }
1599 else if(event.getType() == GShell.SCHEDULE && event.getStatus() == GShell.OK ) {
1600
1601 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.ScheduleBuilt", Dictionary.get("ScheduleBuilt.Title"), Dictionary.get("ScheduleBuilt.Message"), null, false);
1602 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1603 collection_built_warning_dialog.display();
1604 collection_built_warning_dialog.dispose();
1605 collection_built_warning_dialog = null;
1606 }
1607 // If we were running a build, now is when we move files across.
1608 else if(event.getType() == GShell.BUILD && event.getStatus() == GShell.OK) {
1609
1610 if ( CollectionDesignManager.buildcolWasFull() ) {
1611 if(installCollection()) {
1612 // If we have a local library running then ask it to add our newly create collection
1613 if (LocalLibraryServer.isRunning() == true) {
1614 LocalLibraryServer.addCollection(collection.getName());
1615 }
1616 else if (Gatherer.GS3) {
1617 //xiao comment out this: convertToGS3Collection();
1618 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.ADD_COMMAND + collection.getName());
1619 }
1620
1621 // Fire a collection changed first to update the preview etc buttons
1622 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1623
1624 // Now display a message dialog saying its all built
1625 WarningDialog collection_built_warning_dialog = new WarningDialog("warning.CollectionBuilt", Dictionary.get("CollectionBuilt.Title"), Dictionary.get("CollectionBuilt.Message"), null, false);
1626 collection_built_warning_dialog.setMessageOnly(true); // Not a warning
1627 collection_built_warning_dialog.display();
1628 collection_built_warning_dialog.dispose();
1629 collection_built_warning_dialog = null;
1630
1631 //Set nothing as needing rebuilding, as a build has just finished :-)
1632 CollectionDesignManager.resetRebuildTypeRequired();
1633 }
1634 else {
1635 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1636 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1637 DebugStream.println("Status is ok but !installCollection()");
1638 }
1639 }
1640 }
1641 else if (event.getStatus() == GShell.CANCELLED) {
1642 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Build_Cancelled"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1643 Gatherer.g_man.repaint();
1644 }
1645 else if (event.getStatus() == GShell.ERROR) {
1646 if (event.getType() == GShell.NEW) {
1647 String name = event.getMessage();
1648 String collectDir = getCollectionDirectoryPath(name);
1649 String errMsg = "";
1650 if (!new File(getCollectionDirectoryPath(name)).exists() || !new File(getCollectionDirectoryPath(name)).canWrite()) {
1651 String reason = Dictionary.get("FileActions.Write_Not_Permitted_Message", new String[]{collectDir});
1652 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection_With_Reason", new String[]{reason});
1653 if(Gatherer.client_operating_system.toUpperCase().indexOf("WINDOWS") != -1){
1654 //if(Gatherer.client_operating_system.toUpperCase().indexOf("VISTA")!=-1){
1655 errMsg += Dictionary.get("FileActions.File_Permission_Detail", new String[]{Configuration.gsdl_path, System.getProperty("user.name")});
1656 //}
1657 }
1658 } else {
1659 errMsg = Dictionary.get("CollectionManager.Cannot_Create_Collection");
1660 }
1661 JOptionPane.showMessageDialog(Gatherer.g_man, errMsg, Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1662 }
1663 else if(event.getType() == GShell.SCHEDULE) {
1664 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Schedule_Failed"), Dictionary.get("CollectionManager.Schedule_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1665 }
1666 else {
1667 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Preview_Ready_Failed"), Dictionary.get("CollectionManager.Preview_Ready_Title"), JOptionPane.ERROR_MESSAGE);
1668 Gatherer.refresh(Gatherer.COLLECTION_REBUILT);
1669 }
1670
1671 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.
1672 }
1673 }
1674
1675
1676 /** Determine if the manager is ready for actions apon its collection.
1677 * @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.
1678 */
1679 static public synchronized boolean ready() {
1680 if(collection != null) {
1681 return true;
1682 }
1683 else {
1684 return false;
1685 }
1686 }
1687
1688
1689 /** This method associates the collection build monitor with the build monitor created in CreatePane.
1690 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the build monitor.
1691 */
1692 public void registerBuildMonitor(GShellProgressMonitor monitor) {
1693 build_monitor = monitor;
1694 }
1695 /** This method associates the collection import monitor with the import monitor created in CreatePane.
1696 * @param monitor A <strong>GShellProgressMonitor</strong> which we will use as the import monitor.
1697 */
1698 public void registerImportMonitor(GShellProgressMonitor monitor) {
1699 import_monitor = monitor;
1700 }
1701
1702 public void registerScheduleMonitor(GShellProgressMonitor monitor) {
1703 schedule_monitor = monitor;
1704 }
1705
1706
1707 static public void removeCollectionContentsChangedListener(CollectionContentsChangedListener listener)
1708 {
1709 collection_contents_changed_listeners.remove(listener);
1710 }
1711
1712
1713 public void removeMetadataSet(MetadataSet metadata_set)
1714 {
1715 DebugStream.println("Removing metadata set...");
1716
1717 // Delete the .mds file from the collection's "metadata" folder...
1718 File metadata_set_file = metadata_set.getMetadataSetFile();
1719
1720 // ...but not if it is the "ex.mds" file
1721 if (metadata_set_file.getName().equals("ex.mds")) {
1722 return;
1723 }
1724
1725 // ...and only if it exists
1726 if (metadata_set_file.exists()) {
1727 metadata_set_file.delete();
1728
1729 // Unload it from the MetadataSetManager
1730 MetadataSetManager.unloadMetadataSet(metadata_set);
1731
1732 // If we're using a remote Greenstone server, delete the metadata file on the server
1733 if (Gatherer.isGsdlRemote) {
1734 Gatherer.remoteGreenstoneServer.deleteCollectionFile(collection.getGroupQualifiedName(false), metadata_set_file);
1735 }
1736 }
1737 }
1738
1739
1740 /** Used to check whether all open collections have a 'saved' state.
1741 * @return A <i>boolean</i> which is <i>true</i> if the collection has been saved.
1742 * @see org.greenstone.gatherer.collection.Collection
1743 */
1744 public boolean saved() {
1745 boolean result = true;
1746 if(collection != null) {
1747 result = collection.getSaved();
1748 }
1749 return result;
1750 }
1751
1752
1753 /** Saves the currently loaded collection. */
1754 public void saveCollection()
1755 {
1756
1757 if (collection == null) return;
1758
1759 DebugStream.println("Saving collection " + collection.getName() + "...");
1760
1761 // Change cursor to hourglass
1762 Gatherer.g_man.wait(true);
1763
1764 // Create a backup of the collection file, just in case anything goes wrong
1765 File collection_file = new File(getLoadedCollectionColFilePath());
1766 if (collection_file.exists()) {
1767 File collection_file_backup = new File(collection_file.getAbsolutePath() + "~");
1768 if (!collection_file.renameTo(collection_file_backup)) {
1769 DebugStream.println("Error in CollectionManager.saveCollection(): could not create backup file.");
1770 }
1771 collection_file_backup.deleteOnExit();
1772 }
1773
1774 // Write out the collection file
1775 collection.save();
1776
1777 // Write out the collection configuration file
1778 collection.cdm.save();
1779
1780 // Change cursor back to normal
1781 Gatherer.g_man.wait(false);
1782 }
1783
1784
1785 /** 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] */
1786 // now add in greenstone metadata set too.
1787 private void addDefaultMetadataSets()
1788 {
1789 // Add dublin core which is the default metadata set. The user
1790 // can change this later
1791 File dc_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"dublin.mds");
1792 if (dc_file.exists()) {
1793 importMetadataSet(new MetadataSet(dc_file));
1794 }
1795 File gs_file = new File(Gatherer.getGLIMetadataDirectoryPath()+"greenstone.mds");
1796 if (gs_file.exists()) {
1797 importMetadataSet(new MetadataSet(gs_file));
1798 }
1799 }
1800
1801
1802 private void addRequiredMetadataSets()
1803 {
1804 // Always import the extracted metadata set
1805 File extracted_metadata_set_file = new File(Gatherer.getGLIMetadataDirectoryPath() + MetadataSetManager.EXTRACTED_METADATA_NAMESPACE + StaticStrings.METADATA_SET_EXTENSION);
1806 importMetadataSet(new MetadataSet(extracted_metadata_set_file));
1807 }
1808
1809 private String getDefaultCollectDirectory() {
1810 String collect_dir = Gatherer.getCollectDirectoryPath();
1811 // Remove erroneous file windows file separator as it causes problems when running import.pl
1812 if(collect_dir.length() > 2 && collect_dir.endsWith("\\")) {
1813 collect_dir = collect_dir.substring(0, collect_dir.length() - 1);
1814 }
1815 return collect_dir;
1816 }
1817
1818 // used as arg in the perl scripts
1819 private String getCollectDirectory() {
1820 String collect_dir = Gatherer.getCollectDirectoryPath();
1821 return collect_dir.substring(0, collect_dir.length()-1); // remove trailing slash
1822
1823 // the following will stick any colgroup at the end of the collect directory, making it no longer
1824 // possible to get the real collect dir in a general manner if this were located outside greenstone
1825 //String collect_dir = collection.getCollectionDirectory().getParentFile().getPath();
1826 //return collect_dir;
1827 }
1828
1829
1830 /** Install collection by moving its files from building to index after a successful build.
1831 * @see org.greenstone.gatherer.Gatherer
1832 * @see org.greenstone.gatherer.util.Utility
1833 */
1834 private boolean installCollection()
1835 {
1836 if (Configuration.fedora_info.isActive()) {
1837 DebugStream.println("Fedora build complete. No need to move files.");
1838 return true;
1839 }
1840
1841
1842 DebugStream.println("Build complete. Moving files.");
1843
1844 try {
1845 // Ensure that the local library has released this collection so we can delete the index directory
1846 if (LocalLibraryServer.isRunning() == true) {
1847 LocalLibraryServer.releaseCollection(getLoadedCollectionName(true)); // URL style slash //collection.getName());
1848 }
1849 // deactivate it in tomcat so that windows will release the index files
1850 if (Gatherer.GS3 && !Gatherer.isGsdlRemote) {
1851 Gatherer.configGS3Server(Configuration.site_name, ServletConfiguration.DEACTIVATE_COMMAND + collection.getName());
1852 }
1853 File index_dir = new File(getLoadedCollectionIndexDirectoryPath());
1854 DebugStream.println("Index = " + index_dir.getAbsolutePath());
1855
1856 File building_dir = new File(getLoadedCollectionBuildingDirectoryPath());
1857 DebugStream.println("Building = " + building_dir.getAbsolutePath());
1858
1859 // Get the build mode from the build options
1860 String build_mode = collection.build_options.getValue("mode");
1861
1862 // Special case for build mode "all": replace index dir with building dir
1863 if (build_mode == null || build_mode.equals(Dictionary.get("CreatePane.Mode_All"))) {
1864 // Remove the old index directory
1865 if (index_dir.exists()) {
1866 Utility.delete(index_dir);
1867
1868 // Wait for a couple of seconds, just for luck
1869 wait(2000);
1870
1871 // Check the delete worked
1872 if (index_dir.exists()) {
1873 throw new Exception(Dictionary.get("CollectionManager.Index_Not_Deleted"));
1874 }
1875 }
1876
1877 if (Gatherer.isGsdlRemote) {
1878 Gatherer.remoteGreenstoneServer.deleteCollectionFile(
1879 collection.getGroupQualifiedName(false), new File(getLoadedCollectionIndexDirectoryPath()));
1880 Gatherer.remoteGreenstoneServer.moveCollectionFile(collection.getGroupQualifiedName(false),
1881 new File(getLoadedCollectionBuildingDirectoryPath()), new File(getLoadedCollectionIndexDirectoryPath()));
1882 }
1883
1884 // Move the building directory to become the new index directory
1885 if (building_dir.renameTo(index_dir) == false) {
1886 throw new Exception(Dictionary.get("CollectionManager.Build_Not_Moved"));
1887 }
1888 }
1889
1890 // Otherwise copy everything in the building dir into the index dir
1891 else {
1892 moveContentsInto(building_dir, index_dir);
1893 }
1894 }
1895 catch (Exception exception) {
1896 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CollectionManager.Install_Exception", exception.getMessage()), "Error", JOptionPane.ERROR_MESSAGE);
1897 return false;
1898 }
1899 return true;
1900 }
1901
1902
1903 /** Moves all the files in one directory into another, overwriting existing files */
1904 private void moveContentsInto(File source_directory, File target_directory)
1905 {
1906 File[] source_files = source_directory.listFiles();
1907 for (int i = 0; i < source_files.length; i++) {
1908 File source_file = source_files[i];
1909 File target_file = new File(target_directory, source_file.getName());
1910
1911 if (source_file.isDirectory()) {
1912 moveContentsInto(source_file, target_file);
1913 source_file.delete();
1914 }
1915 else {
1916 if (target_file.exists()) {
1917 target_file.delete();
1918 }
1919
1920 source_file.renameTo(target_file);
1921 }
1922 }
1923 }
1924
1925 private void updateCollectionConfigXML(File base_cfg, File new_cfg) {
1926 //In this method, the files base_cfg and new_cfg are all xml files.
1927
1928 Document base_cfg_doc = XMLTools.parseXMLFile(base_cfg);
1929 XMLTools.writeXMLFile(new_cfg, base_cfg_doc);
1930 Document new_cfg_doc = XMLTools.parseXMLFile(new_cfg);
1931 Element collection_config = new_cfg_doc.getDocumentElement();
1932
1933 Node browseNode = XMLTools.getChildByTagNameIndexed(collection_config, StaticStrings.BROWSE_STR, 0);
1934 NodeList classifier_children = ((Element)browseNode).getElementsByTagName(StaticStrings.CLASSIFIER_STR);
1935 int num_nodes = classifier_children.getLength();
1936
1937 if (num_nodes < 1) {
1938 return;
1939 }
1940
1941 // Read in the classifier command watching for hfile, metadata and sort arguments.
1942 String buttonname = null;
1943 String hfile = null;
1944 String metadata = null;
1945 String sort = null;
1946
1947 for (int i=0; i<num_nodes; i++) {
1948 Element classifier_element = (Element)classifier_children.item(i);
1949 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
1950 for (int j=0; j<option_children.getLength(); j++) {
1951 Element option_element = (Element)option_children.item(j);
1952 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
1953 String value_str = option_element.getAttribute(StaticStrings.VALUE_ATTRIBUTE);
1954
1955 if (name_str == null || name_str.equals("")) {
1956 continue;
1957 }
1958 if (name_str != null && value_str == null ) {
1959 value_str = "";
1960 }
1961 if (name_str.equals("hfile")) {
1962 hfile = value_str;
1963 }
1964 else if (name_str.equals("metadata") && value_str != null) {
1965 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
1966 if (replacement != null && !replacement.equals("")) {
1967 metadata = replacement;
1968 }
1969 }
1970 else if (name_str.equals("sort") && value_str != null) {
1971 String replacement = ProfileXMLFileManager.getMetadataElementFor(value_str);
1972 if (replacement != null && !replacement.equals("")) {
1973 sort = replacement;
1974 }
1975 }
1976 else if(name_str.equals("buttonname") && value_str != null) {
1977 buttonname = value_str;
1978 }
1979 }
1980 }
1981 for (int i=0; i<num_nodes; i++) {
1982 Element classifier_element = (Element)classifier_children.item(i);
1983 NodeList option_children = classifier_element.getElementsByTagName(StaticStrings.OPTION_STR);
1984 for (int j=0; j<option_children.getLength(); j++) {
1985 Element option_element = (Element)option_children.item(j);
1986 String name_str = option_element.getAttribute(StaticStrings.NAME_ATTRIBUTE);
1987
1988 if (name_str.equals("metadata") && metadata != null) {
1989 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
1990 }
1991 else if (name_str.equals("hfile") && hfile != null) {
1992 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata + ".txt");
1993 }
1994 else if (name_str.equals("sort") && sort != null) {
1995 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, sort);
1996 }
1997 else if(name_str.equals("buttonname") && (buttonname == "" || buttonname == null)) {
1998 // No buttonname has been specified. Lets create one using the metadata as its value
1999 Element option = new_cfg_doc.createElement(StaticStrings.OPTION_STR);
2000 option.setAttribute(StaticStrings.NAME_ATTRIBUTE, "buttonname");
2001 option_element.setAttribute(StaticStrings.VALUE_ATTRIBUTE, metadata);
2002 classifier_element.appendChild(option);
2003 }
2004 }
2005 }
2006 }
2007
2008 private void updateCollectionCFG(File base_cfg, File new_cfg, String description, String email, String title)
2009 {
2010 boolean first_name = true;
2011 boolean first_extra = true;
2012
2013 // 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.
2014 try {
2015 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(base_cfg), "UTF-8"));
2016 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new_cfg), "UTF-8"));
2017 String command = null;
2018 while((command = in.readLine()) != null) {
2019 if (command.length()==0) {
2020 // output a new line
2021 out.newLine();
2022 continue;
2023 }
2024 // We have to test the end of command for the special character '\'. If found, remove it and append the next line, then repeat.
2025 while(command.trim().endsWith("\\")) {
2026 command = command.substring(0, command.lastIndexOf("\\"));
2027 String next_line = in.readLine();
2028 if(next_line != null) {
2029 command = command + next_line;
2030 }
2031 }
2032 // commands can extend over more than one line so use the CommandTokenizer which takes care of that
2033 CommandTokenizer tokenizer = new CommandTokenizer(command, in, false);
2034 String command_type_str = tokenizer.nextToken().toLowerCase();
2035
2036 if (command_type_str.equals(StaticStrings.COLLECTIONMETADATA_STR)) {
2037 // read the whole thing in, but for collectionname, collectionextra, iconcollection, iconcollectionsmall we will ignore them
2038 StringBuffer new_command = new StringBuffer(command_type_str);
2039 String meta_name = tokenizer.nextToken();
2040 new_command.append(' ');
2041 new_command.append(meta_name);
2042 while (tokenizer.hasMoreTokens()) {
2043 new_command.append(' ');
2044 new_command.append(tokenizer.nextToken());
2045 }
2046 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)) {
2047 // dont save
2048 } else {
2049 write(out, new_command.toString());
2050 }
2051 new_command = null;
2052 continue;
2053 } // if collectionmeta
2054
2055 if (command_type_str.equals("classify")) {
2056 StringBuffer text = new StringBuffer(command_type_str);
2057 // Read in the classifier command watching for hfile, metadata and sort arguments.
2058 String buttonname = null;
2059 String hfile = null;
2060 String new_metadata = null;
2061 String old_metadata = null;
2062
2063 while(tokenizer.hasMoreTokens()) {
2064 String token = tokenizer.nextToken();
2065 if (token.equals("-hfile")) {
2066 if(tokenizer.hasMoreTokens()) {
2067 text.append(" ");
2068 text.append(token);
2069 token = tokenizer.nextToken();
2070 hfile = token;
2071 }
2072 }
2073 else if (token.equals("-metadata")) {
2074 if(tokenizer.hasMoreTokens()) {
2075 text.append(" ");
2076 text.append(token);
2077 String temp_metadata = tokenizer.nextToken();
2078 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2079 if (replacement != null && !replacement.equals("")) {
2080 token = replacement;
2081 old_metadata = temp_metadata;
2082 new_metadata = replacement;
2083 }
2084 else {
2085 token = temp_metadata;
2086 }
2087 temp_metadata = null;
2088 replacement = null;
2089 }
2090 }
2091 else if (token.equals("-sort")) {
2092 if(tokenizer.hasMoreTokens()) {
2093 text.append(" ");
2094 text.append(token);
2095 String temp_metadata = tokenizer.nextToken();
2096 String replacement = ProfileXMLFileManager.getMetadataElementFor(temp_metadata);
2097 if (replacement != null && !replacement.equals("")) {
2098 token = replacement;
2099 }
2100 else {
2101 token = temp_metadata;
2102 }
2103 temp_metadata = null;
2104 replacement = null;
2105 }
2106 }
2107 else if(token.equals("-buttonname")) {
2108 buttonname = token;
2109 }
2110 text.append(' ');
2111 text.append(token);
2112 token = null;
2113 }
2114
2115 // 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)!
2116 if(old_metadata != null && new_metadata != null && buttonname == null) {
2117 text.append(' ');
2118 text.append("-buttonname");
2119 text.append(' ');
2120 text.append(old_metadata);
2121 }
2122 command = text.toString();
2123 // Replace the hfile if we found it
2124 if(hfile != null && new_metadata != null) {
2125 command = command.replaceAll(hfile, new_metadata + ".txt");
2126 }
2127
2128 buttonname = null;
2129 hfile = null;
2130 new_metadata = null;
2131 old_metadata = null;
2132 write(out, command);
2133 } else {
2134 // the rest of the commands just want a string - we read in all the tokens from the tokeniser and get rid of it.
2135 StringBuffer new_command = new StringBuffer(command_type_str);
2136 while (tokenizer.hasMoreTokens()) {
2137 new_command.append(' ');
2138 new_command.append(tokenizer.nextToken());
2139 }
2140
2141 command = new_command.toString();
2142
2143 // 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.
2144 // we really want to build up the whole command here
2145 boolean format_command = command_type_str.equals("format");
2146 HashMap metadata_mapping = ProfileXMLFileManager.getMetadataMapping();
2147 if (metadata_mapping != null) {
2148 Iterator keys = metadata_mapping.keySet().iterator();
2149 while (keys.hasNext()) {
2150 String target = (String) keys.next();
2151 String replacement = (String) metadata_mapping.get(target);
2152 if (replacement != null && !replacement.equals("")) {
2153 if (format_command) {
2154 target = "\\[" + target + "\\]";
2155 replacement = "{Or}{[" + replacement + "]," + target + "}";
2156 }
2157 command = command.replaceAll(target, replacement);
2158 }
2159 }
2160 }
2161
2162 write(out, command);
2163 }
2164 tokenizer = null;
2165 }
2166 in.close();
2167 in = null;
2168 out.flush();
2169 out.close();
2170 out = null;
2171 }
2172 catch(Exception error) {
2173 DebugStream.printStackTrace(error);
2174 }
2175 // All done, I hope.
2176 }
2177
2178 private void write(BufferedWriter out, String message)
2179 throws Exception {
2180 out.write(message, 0, message.length());
2181 out.newLine();
2182 }
2183
2184
2185 /** The CollectionManager class is getting too confusing by half so I'll implement this TreeModelListener in a private class to make responsibility clear. */
2186 private class FMTreeModelListener
2187 implements TreeModelListener {
2188 /** 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.
2189 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2190 */
2191 public void treeNodesChanged(TreeModelEvent event) {
2192 if(collection != null) {
2193 collection.setSaved(false);
2194 collection.setFilesChanged(true);
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 treeNodesInserted(TreeModelEvent event) {
2201 if(collection != null) {
2202 collection.setSaved(false);
2203 collection.setFilesChanged(true);
2204 }
2205 }
2206 /** 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.
2207 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2208 */
2209 public void treeNodesRemoved(TreeModelEvent event) {
2210 if(collection != null) {
2211 collection.setSaved(false);
2212 collection.setFilesChanged(true);
2213
2214 }
2215 }
2216 /** 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.
2217 * @param event A <strong>TreeModelEvent</strong> encompassing all the information about the event which has changed the tree.
2218 */
2219 public void treeStructureChanged(TreeModelEvent event) {
2220 if(collection != null) {
2221 collection.setSaved(false);
2222 }
2223 }
2224 }
2225}
Note: See TracBrowser for help on using the repository browser.