source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Construct.java@ 31124

Last change on this file since 31124 was 31124, checked in by ak19, 7 years ago
  1. Dr Bainbridge found another oversight in the existing code. 2. Previous commit does not yet fully solve the concurrency bug noticed when setting multiple pieces of metadata in one go.
  • Property svn:keywords set to Author Date Id Revision
File size: 35.6 KB
Line 
1/*
2 * GS2Construct.java
3 * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3.service;
20
21import java.io.BufferedWriter;
22import java.io.File;
23import java.io.FileWriter;
24import java.io.Serializable;
25import java.util.Collections;
26import java.util.Iterator;
27import java.util.Map.Entry;
28import java.util.HashMap;
29import java.util.Map;
30import java.util.Set;
31
32import org.apache.log4j.Logger;
33import org.greenstone.gsdl3.build.GS2PerlConstructor;
34import org.greenstone.gsdl3.build.GS2PerlListener;
35import org.greenstone.gsdl3.util.GSFile;
36import org.greenstone.gsdl3.util.GSParams;
37import org.greenstone.gsdl3.util.GSPath;
38import org.greenstone.gsdl3.util.GSStatus;
39import org.greenstone.gsdl3.util.GSXML;
40import org.greenstone.gsdl3.util.OID;
41import org.greenstone.gsdl3.util.SimpleCollectionDatabase;
42import org.greenstone.gsdl3.util.UserContext;
43import org.greenstone.gsdl3.util.XMLConverter;
44
45import org.w3c.dom.Document;
46import org.w3c.dom.Element;
47import org.w3c.dom.Node;
48import org.w3c.dom.Text;
49
50/**
51 * A Services class for building collections provides a wrapper around the old
52 * perl scripts
53 *
54 * @author Katherine Don
55 */
56public class GS2Construct extends ServiceRack
57{
58
59 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.GS2Construct.class.getName());
60
61 // services offered
62 private static final String NEW_SERVICE = "NewCollection";
63 private static final String ADD_DOC_SERVICE = "AddDocument";
64 private static final String IMPORT_SERVICE = "ImportCollection";
65 private static final String BUILD_SERVICE = "BuildCollection";
66 private static final String ACTIVATE_SERVICE = "ActivateCollection";
67 private static final String BUILD_AND_ACTIVATE_SERVICE = "BuildAndActivateCollection";
68 private static final String DELETE_SERVICE = "DeleteCollection";
69 private static final String RELOAD_SERVICE = "ReloadCollection";
70 private static final String MODIFY_METADATA_SERVICE = "ModifyMetadata"; // set or remove metadata
71
72
73 // params used
74 private static final String COL_PARAM = "collection";
75 private static final String NEW_COL_TITLE_PARAM = "collTitle";
76 private static final String NEW_COL_ABOUT_PARAM = "collAbout";
77 private static final String CREATOR_PARAM = "creator";
78 private static final String NEW_FILE_PARAM = "newfile";
79 private static final String PROCESS_ID_PARAM = GSParams.PROCESS_ID;
80 private static final String BUILDTYPE_PARAM = "buildType";
81 private static final String BUILDTYPE_MG = "mg";
82 private static final String BUILDTYPE_MGPP = "mgpp";
83
84 protected static String DATABASE_TYPE = null;
85 protected SimpleCollectionDatabase coll_db = null;
86
87 // the list of the collections - store between some method calls
88 private String[] collection_list = null;
89
90 // set of listeners for any construction commands
91 protected Map<String, GS2PerlListener> listeners = null;
92 protected HashMap<String, Boolean> collectionOperationMap = new HashMap<String, Boolean>();
93
94 public GS2Construct()
95 {
96 this.listeners = Collections.synchronizedMap(new HashMap<String, GS2PerlListener>());
97 }
98
99 /** returns a specific service description */
100 protected Element getServiceDescription(Document doc, String service, String lang, String subset)
101 {
102
103 Element description = doc.createElement(GSXML.SERVICE_ELEM);
104 description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
105 description.setAttribute(GSXML.NAME_ATT, service);
106 if (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER))
107 {
108 description.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_NAME, getTextString(service + ".name", lang)));
109 description.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getTextString(service + ".description", lang)));
110 description.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_SUBMIT, getTextString(service + ".submit", lang)));
111 }
112 if (subset == null || subset.equals(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER))
113 {
114 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
115 description.appendChild(param_list);
116
117 if (service.equals(NEW_SERVICE))
118 {
119
120 Element param = GSXML.createParameterDescription(doc, NEW_COL_TITLE_PARAM, getTextString("param." + NEW_COL_TITLE_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
121 param_list.appendChild(param);
122 param = GSXML.createParameterDescription(doc, CREATOR_PARAM, getTextString("param." + CREATOR_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
123 param_list.appendChild(param);
124 param = GSXML.createParameterDescription(doc, NEW_COL_ABOUT_PARAM, getTextString("param." + NEW_COL_ABOUT_PARAM, lang), GSXML.PARAM_TYPE_TEXT, null, null, null);
125 param_list.appendChild(param);
126 String[] types = { BUILDTYPE_MGPP, BUILDTYPE_MG };
127 String[] type_texts = { getTextString("param." + BUILDTYPE_PARAM + "." + BUILDTYPE_MGPP, lang), getTextString("param." + BUILDTYPE_PARAM + "." + BUILDTYPE_MG, lang) };
128
129 param = GSXML.createParameterDescription(doc, BUILDTYPE_PARAM, getTextString("param." + BUILDTYPE_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, BUILDTYPE_MGPP, types, type_texts);
130 param_list.appendChild(param);
131 }
132 else if (service.equals(ACTIVATE_SERVICE) || service.equals(IMPORT_SERVICE) || service.equals(BUILD_SERVICE) || service.equals(RELOAD_SERVICE) || service.equals(DELETE_SERVICE) || service.equals(MODIFY_METADATA_SERVICE))
133 {
134
135 this.collection_list = getCollectionList();
136 Element param = GSXML.createParameterDescription(doc, COL_PARAM, getTextString("param." + COL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, null, this.collection_list, this.collection_list);
137 param_list.appendChild(param);
138 }
139 else
140 {
141 // invalid service name
142 return null;
143 }
144 }
145 return description;
146 }
147
148 // each service must have a method "process<New service name>"
149
150 protected Element processNewCollection(Element request)
151 {
152 if (!userHasCollectionEditPermissions(request)) {
153 Document result_doc = XMLConverter.newDOM();
154 Element result = GSXML.createBasicResponse(result_doc, "processNewCollection");
155 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
156 return result;
157 }
158 return runCommand(request, GS2PerlConstructor.NEW);
159 }
160
161 /** TODO:implement this */
162 protected Element processAddDocument(Element request)
163 {
164 if (!userHasCollectionEditPermissions(request)) {
165 Document result_doc = XMLConverter.newDOM();
166 Element result = GSXML.createBasicResponse(result_doc, "processAddDocument");
167 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
168 return result;
169 }
170
171 Document result_doc = XMLConverter.newDOM();
172 // decode the file name, add it to the import directory
173 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
174 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
175 response.setAttribute(GSXML.FROM_ATT, name);
176 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
177 response.appendChild(status);
178 //String lang = request.getAttribute(GSXML.LANG_ATT);
179 //String request_type = request.getAttribute(GSXML.TYPE_ATT);
180 Text t = result_doc.createTextNode("AddDocument: not implemented yet");
181 status.appendChild(t);
182 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
183 return response;
184 }
185
186 protected Element processBuildAndActivateCollection(Element request)
187 {
188 // check permissions
189 if (!userHasCollectionEditPermissions(request)) {
190 Document result_doc = XMLConverter.newDOM();
191 Element result = GSXML.createBasicResponse(result_doc, "processBuildAndActivateCollection");
192 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
193 return result;
194 }
195
196
197 waitUntilReady(request);
198 Element buildResponse = processBuildCollection(request);
199 if (buildResponse.getElementsByTagName(GSXML.ERROR_ELEM).getLength() > 0)
200 {
201 signalReady(request);
202 return buildResponse;
203 }
204
205 Element statusElem = (Element) buildResponse.getElementsByTagName(GSXML.STATUS_ELEM).item(0);
206 String id = statusElem.getAttribute("pid");
207
208 GS2PerlListener currentListener = this.listeners.get(id);
209 int statusCode = currentListener.getStatus();
210 while (!GSStatus.isCompleted(statusCode))
211 {
212 // wait for the process, and keep checking the status code
213 // there is probably a better way to do this.
214 try
215 {
216 Thread.currentThread().sleep(100);
217 }
218 catch (Exception e)
219 { // ignore
220 }
221 statusCode = currentListener.getStatus();
222 }
223
224 Element activateResponse = processActivateCollection(request);
225 signalReady(request);
226 return activateResponse;
227 }
228
229 protected Element processImportCollection(Element request)
230 {
231 if (!userHasCollectionEditPermissions(request)) {
232 Document result_doc = XMLConverter.newDOM();
233 Element result = GSXML.createBasicResponse(result_doc, "processImportCollection");
234 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
235 return result;
236 }
237
238 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
239 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
240
241 if (params == null)
242 {
243 return null;
244 }
245
246 //If we have been requested to only build certain documents then we need to create a manifest file
247 String documentsParam = (String) params.get("documents");
248 if (documentsParam != null && !documentsParam.equals(""))
249 {
250 String s = File.separator;
251 String manifestFolderPath = this.site_home + s + "collect" + s + params.get(COL_PARAM) + s + "manifests";
252 String manifestFilePath = manifestFolderPath + File.separator + "tempManifest.xml";
253
254 File manifestFolderFile = new File(manifestFolderPath);
255 if (!manifestFolderFile.exists())
256 {
257 manifestFolderFile.mkdirs();
258 }
259
260 File manifestFile = new File(manifestFilePath);
261 if (!manifestFile.exists())
262 {
263 try
264 {
265 manifestFile.createNewFile();
266 }
267 catch (Exception ex)
268 {
269 ex.printStackTrace();
270 return null; //Probably should return an actual error
271 }
272 }
273 String[] docList = documentsParam.split(",");
274
275 try
276 {
277 BufferedWriter bw = new BufferedWriter(new FileWriter(manifestFile));
278 bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
279 bw.write("<Manifest>\n");
280 bw.write(" <Index>\n");
281 for (int j = 0; j < docList.length; j++)
282 {
283 bw.write(" <Filename>" + docList[j] + "</Filename>\n");
284 }
285 bw.write(" </Index>\n");
286 bw.write("</Manifest>\n");
287 bw.close();
288 }
289 catch (Exception ex)
290 {
291 ex.printStackTrace();
292 return null; //Probably should return an actual error
293 }
294 }
295
296 return runCommand(request, GS2PerlConstructor.IMPORT);
297 }
298
299 protected Element processBuildCollection(Element request)
300 {
301 if (!userHasCollectionEditPermissions(request)) {
302 Document result_doc = XMLConverter.newDOM();
303 Element result = GSXML.createBasicResponse(result_doc, "processBuildCollection");
304 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
305 return result;
306 }
307
308 return runCommand(request, GS2PerlConstructor.BUILD);
309 }
310
311 protected Element processModifyMetadata(Element request)
312 {
313 if (!userHasCollectionEditPermissions(request)) {
314 Document result_doc = XMLConverter.newDOM();
315 Element result = GSXML.createBasicResponse(result_doc, "processModifyMetadata");
316 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
317 return result;
318 }
319
320 // wait until we can reserve the collection for processing
321 waitUntilReady(request);
322
323 // process
324 Element response = runCommand(request, GS2PerlConstructor.MODIFY_METADATA_SERVER);
325
326 // release hold on collection
327 signalReady(request);
328 return response;
329 }
330
331 protected Element processActivateCollection(Element request)
332 {
333
334 if (!userHasCollectionEditPermissions(request)) {
335 Document result_doc = XMLConverter.newDOM();
336 Element result = GSXML.createBasicResponse(result_doc, "processActivateCollection");
337 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
338 return result;
339 }
340
341 // this activates the collection on disk. but now we need to tell
342 // the MR about it. but we have to wait until the process is finished.
343 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
344 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
345 String coll_name = (String) params.get(COL_PARAM);
346 String lang = request.getAttribute(GSXML.LANG_ATT);
347
348 UserContext userContext = new UserContext(request);
349 String request_type = request.getAttribute(GSXML.TYPE_ATT);
350
351 // now we de-activate the collection before running activate.pl, and then re-activate at end
352 // So activate.pl only does the moving, no activation. This way will prevent java from launching
353 // perl, exiting and then leaving dangling file handles (on index/text/col.gdb) in perl.
354 if (!request_type.equals(GSXML.REQUEST_TYPE_STATUS)) {
355 systemRequest("delete", coll_name, null, userContext); // deactivate collection
356 }
357
358 Element response = runCommand(request, GS2PerlConstructor.ACTIVATE); // if request is for STATUS, then this won't run activate.pl
359
360 Element status = (Element) GSXML.getChildByTagName(response, GSXML.STATUS_ELEM);
361
362 // check for finished
363 int status_code = Integer.parseInt(status.getAttribute(GSXML.STATUS_ERROR_CODE_ATT));
364 if (GSStatus.isCompleted(status_code) && GSStatus.isError(status_code))
365 {
366 // we shouldn't carry out the next bit, just return the response
367 return response;
368 }
369 String id = status.getAttribute(GSXML.STATUS_PROCESS_ID_ATT);
370 GS2PerlListener listener = this.listeners.get(id);
371 if (listener == null)
372 {
373 logger.error("somethings gone wrong, couldn't find the listener");
374 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
375 return response;
376 }
377
378 while (!GSStatus.isCompleted(status_code))
379 {
380 // wait for the process, and keep checking the status code
381 // there is probably a better way to do this.
382 try
383 {
384 Thread.currentThread().sleep(100);
385 }
386 catch (Exception e)
387 { // ignore
388 }
389 status_code = listener.getStatus();
390 }
391
392 // add the rest of the messages to the status node
393 Text t = status.getOwnerDocument().createTextNode("\n" + listener.getUpdate());
394 status.appendChild(t);
395 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
396 if (GSStatus.isError(status_code))
397 {
398 return response; // without doing the next bit
399 }
400
401 t = status.getOwnerDocument().createTextNode("\n");
402 status.appendChild(t);
403 // once have got here, we assume
404 // the first bit proceeded successfully, now reload the collection (sends a collection reactivation request)
405 systemRequest("reload", coll_name, status, userContext); // this will append more messages to the status, and overwrite the error code att
406 return response;
407
408 }
409
410 protected Element processDeleteCollection(Element request)
411 {
412 if (!userHasCollectionEditPermissions(request)) {
413 Document result_doc = XMLConverter.newDOM();
414 Element result = GSXML.createBasicResponse(result_doc, "processDeleteCollection");
415 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
416 return result;
417 }
418
419 Document result_doc = XMLConverter.newDOM();
420 // the response to send back
421 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
422 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
423 response.setAttribute(GSXML.FROM_ATT, name);
424 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
425 response.appendChild(status);
426 Text t = null; // the text node for the error/success message
427 String lang = request.getAttribute(GSXML.LANG_ATT);
428 String request_type = request.getAttribute(GSXML.TYPE_ATT);
429
430 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
431 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
432
433 boolean get_status_only = false;
434 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
435 {
436 get_status_only = true;
437 }
438 if (get_status_only)
439 {
440 // at the moment, delete is synchronous. but it may take ages so should do the command in another thread maybe? in which case we will want to ask for status
441 logger.error("had a status request for delete - this shouldn't happen!!");
442 //t = result_doc.createTextNode("");
443 //status.appendChild(t);
444 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
445 return response;
446 }
447 String coll_name = (String) params.get(COL_PARAM);
448 String[] args = { coll_name };
449 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, coll_name));
450 // check that the coll is there in the first place
451 if (!coll_dir.exists())
452 {
453 t = result_doc.createTextNode(getTextString("delete.exists_error", lang, args));
454 status.appendChild(t);
455 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
456 return response;
457 }
458
459 // try to delete the directory
460 if (!GSFile.deleteFile(coll_dir))
461 {
462 t = result_doc.createTextNode(getTextString("delete.delete_error", lang, args));
463 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
464 status.appendChild(t);
465 return response;
466 }
467
468 UserContext userContext = new UserContext(request);
469
470 systemRequest("delete", coll_name, status, userContext);
471 return response;
472 }
473
474 protected Element processReloadCollection(Element request)
475 {
476 if (!userHasCollectionEditPermissions(request)) {
477 Document result_doc = XMLConverter.newDOM();
478 Element result = GSXML.createBasicResponse(result_doc, "processReloadCollection");
479 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
480 return result;
481 }
482
483 Document result_doc = XMLConverter.newDOM();
484 // the response to send back
485 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
486 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
487 response.setAttribute(GSXML.FROM_ATT, name);
488 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
489 response.appendChild(status);
490 Text t = null; // the text node for the error/success message
491
492 String lang = request.getAttribute(GSXML.LANG_ATT);
493 String request_type = request.getAttribute(GSXML.TYPE_ATT);
494
495 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
496 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
497
498 boolean get_status_only = false;
499 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
500 {
501 get_status_only = true;
502 }
503 if (get_status_only)
504 {
505 // reload is synchronous - this makes no sense
506 logger.error("had a status request for reload - this shouldn't happen!!");
507 //t = result_doc.createTextNode("");
508 //status.appendChild(t);
509 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
510 return response;
511 }
512
513 String coll_name = (String) params.get(COL_PARAM);
514
515 UserContext userContext = new UserContext(request);
516
517 systemRequest("reload", coll_name, status, userContext);
518 return response;
519
520 }
521
522 /**
523 * send a configure request to the message router action name should be
524 * "delete" or "reload" response will be put into the status element
525 */
526 protected void systemRequest(String operation, String coll_name, Element status, UserContext userContext)
527 {
528 // send the request to the MR
529 Document doc = XMLConverter.newDOM();
530 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
531 Element request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_SYSTEM, "", userContext);
532 message.appendChild(request);
533 Element command = doc.createElement(GSXML.SYSTEM_ELEM);
534 request.appendChild(command);
535 command.setAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT, GSXML.COLLECTION_ELEM);
536 command.setAttribute(GSXML.SYSTEM_MODULE_NAME_ATT, coll_name);
537
538 if (operation.equals("delete"))
539 {
540 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_DEACTIVATE);
541 }
542 else if (operation.equals("reload"))
543 {
544 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_ACTIVATE);
545 }
546 else
547 {
548 logger.error("invalid action name passed to systemRequest:" + operation);
549 return;
550 }
551 request.appendChild(command);
552 Node response = this.router.process(message); // at the moment, get no info in response so ignore it
553 Text t;
554 String[] args = { coll_name };
555
556 if (status != null)
557 {
558 if (response == null)
559 {
560 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".configure_error", userContext.getLanguage(), args));
561 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
562 status.appendChild(t);
563 return;
564 }
565
566 // if we got here, we have succeeded!
567 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".success", userContext.getLanguage(), args));
568 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.SUCCESS));
569 status.appendChild(t);
570 }
571 }
572
573 /**
574 * configure the service module for now, all services have type=build - need
575 * to think about this
576 */
577 public boolean configure(Element info, Element extra_info)
578 {
579 if (!super.configure(info, extra_info))
580 {
581 return false;
582 }
583
584 logger.info("configuring GS2Construct");
585
586 Element e = null;
587 // hard code in the services for now
588
589 // set up short_service_info_ - for now just has name and type
590
591 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
592 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
593 e.setAttribute(GSXML.NAME_ATT, NEW_SERVICE);
594 this.short_service_info.appendChild(e);
595
596 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
597 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
598 e.setAttribute(GSXML.NAME_ATT, IMPORT_SERVICE);
599 this.short_service_info.appendChild(e);
600
601 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
602 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
603 e.setAttribute(GSXML.NAME_ATT, BUILD_SERVICE);
604 this.short_service_info.appendChild(e);
605
606 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
607 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
608 e.setAttribute(GSXML.NAME_ATT, ACTIVATE_SERVICE);
609 this.short_service_info.appendChild(e);
610
611 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
612 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
613 e.setAttribute(GSXML.NAME_ATT, BUILD_AND_ACTIVATE_SERVICE);
614 this.short_service_info.appendChild(e);
615
616 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
617 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
618 e.setAttribute(GSXML.NAME_ATT, DELETE_SERVICE);
619 this.short_service_info.appendChild(e);
620
621 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
622 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
623 e.setAttribute(GSXML.NAME_ATT, RELOAD_SERVICE);
624 this.short_service_info.appendChild(e);
625
626 //e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
627 //e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
628 //e.setAttribute(GSXML.NAME_ATT, ADD_DOC_SERVICE);
629 //this.short_service_info.appendChild(e);
630
631 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
632 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
633 e.setAttribute(GSXML.NAME_ATT, MODIFY_METADATA_SERVICE);
634 this.short_service_info.appendChild(e);
635
636 return true;
637 }
638
639 /** returns a response element */
640 protected Element runCommand(Element request, int type)
641 {
642 Document result_doc = XMLConverter.newDOM();
643 // the response to send back
644 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
645 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
646 response.setAttribute(GSXML.FROM_ATT, name);
647 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
648 response.appendChild(status);
649
650 String lang = request.getAttribute(GSXML.LANG_ATT);
651 String request_type = request.getAttribute(GSXML.TYPE_ATT);
652
653 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
654 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
655
656 boolean get_status_only = false;
657 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
658 {
659 get_status_only = true;
660 }
661
662 // just check for status messages if that's all that's required
663 if (get_status_only)
664 {
665 String id = (String) params.get(PROCESS_ID_PARAM);
666 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
667 GS2PerlListener listener = this.listeners.get(id);
668 if (listener == null)
669 {
670 Text t = result_doc.createTextNode(getTextString("general.process_id_error", lang));
671 status.appendChild(t);
672 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
673 }
674 else
675 {
676 Text t = result_doc.createTextNode(listener.getUpdate());
677 status.appendChild(t);
678 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
679 // check that we actually should be removing the listener here
680 if (listener.isFinished())
681 { // remove this listener - its job is done
682 this.listeners.remove(id); // not working
683 }
684 }
685 return response;
686
687 }
688
689 // do the actual command
690 String coll_name = null;
691 if (type == GS2PerlConstructor.NEW)
692 {
693 String coll_title = (String) params.get(NEW_COL_TITLE_PARAM);
694 coll_name = createNewCollName(coll_title);
695 }
696 else
697 {
698 coll_name = (String) params.get(COL_PARAM);
699 }
700
701 // makes a paramList of the relevant params
702 Element other_params = extractOtherParams(params, type);
703
704 //create the constructor to do the work
705 GS2PerlConstructor constructor = new GS2PerlConstructor("perl_build");
706 if (!constructor.configure())
707 {
708 Text t = result_doc.createTextNode(getTextString("general.configure_constructor_error", lang));
709 status.appendChild(t);
710 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
711 return response;
712 }
713
714 constructor.setSiteHome(this.site_home);
715 constructor.setCollectionName(coll_name);
716 constructor.setActionType(type);
717 constructor.setProcessParams(other_params);
718 if (type == GS2PerlConstructor.IMPORT)
719 {
720 constructor.setManifestFile(this.site_home + File.separator + "collect" + File.separator + params.get(COL_PARAM) + File.separator + "manifests" + File.separator + "tempManifest.xml");
721 }
722 else if (type == GS2PerlConstructor.MODIFY_METADATA_SERVER) {
723 StringBuffer querystring = new StringBuffer();
724
725 // convert params into a single string again?
726 Set<Map.Entry<String, Serializable>> entries = params.entrySet();
727 Iterator<Map.Entry<String, Serializable>> i = entries.iterator();
728
729 String oid = null;
730
731 while (i.hasNext()) {
732
733 Map.Entry<String, Serializable> entry = i.next();
734 String paramname = entry.getKey();
735 paramname = paramname.replace("s1.", ""); // replaces all
736 // occurrences
737 if (paramname.equals("collection")) {
738 paramname = "c";
739 }
740 if (paramname.equals("d")){
741 oid = (String) entry.getValue();
742 }
743 String paramvalue = (String) entry.getValue();
744
745 querystring.append(paramname + "=" + paramvalue);
746 if (i.hasNext()) {
747 querystring.append("&");
748 }
749 }
750
751 markDocumentInFlatDatabase("R", coll_name, OID.getTop(oid));
752
753 constructor.setQueryString(querystring.toString());
754 }
755
756 GS2PerlListener listener = new GS2PerlListener();
757 constructor.addListener(listener);
758 constructor.start();
759
760 String id = newID();
761 this.listeners.put(id, listener);
762
763 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
764 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ACCEPTED));
765 Text t = result_doc.createTextNode(getTextString("general.process_start", lang));
766 status.appendChild(t);
767 return response;
768 }
769
770 //************************
771 // some helper functions
772 //************************
773
774 /** parse the collect directory and return a list of collection names */
775 protected String[] getCollectionList()
776 {
777
778 File collectDir = new File(GSFile.collectDir(this.site_home));
779 if (!collectDir.exists())
780 {
781 logger.error("couldn't find collect dir: " + collectDir.toString());
782 return null;
783 }
784 logger.info("GS2Construct: reading thru directory " + collectDir.getPath() + " to find collections.");
785 File[] contents = collectDir.listFiles();
786 int num_colls = 0;
787 for (int i = 0; i < contents.length; i++)
788 {
789 if (contents[i].isDirectory() && !contents[i].getName().startsWith("CVS"))
790 {
791 num_colls++;
792 }
793 }
794
795 String[] names = new String[num_colls];
796
797 for (int i = 0, j = 0; i < contents.length; i++)
798 {
799 if (contents[i].isDirectory())
800 {
801 String colName = contents[i].getName();
802 if (!colName.startsWith("CVS"))
803 {
804 names[j] = colName;
805 j++;
806 }
807
808 }
809 }
810
811 return names;
812
813 }
814
815 /** ids used for process id */
816 private int current_id = 0;
817
818 private String newID()
819 {
820 current_id++;
821 return Integer.toString(current_id);
822 }
823
824 /** creates a new short name from the collection title */
825 protected String createNewCollName(String coll_title)
826 {
827
828 String base_name = null;
829 // take the first 6 letters
830 if (coll_title.length() < 6)
831 {
832 base_name = coll_title;
833 }
834 else
835 {
836 base_name = coll_title.substring(0, 6);
837 }
838 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name));
839 if (!coll_dir.exists())
840 { // this name is ok - not used yet
841 return base_name;
842 }
843
844 // now we have to make a new name until we get a good one
845 // try name1, name2 name3 etc
846 int i = 0;
847 while (coll_dir.exists())
848 {
849 i++;
850 coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name + Integer.toString(i)));
851 }
852 return base_name + Integer.toString(i);
853
854 }
855
856 /**
857 * takes the params from the request (in the HashMap) and extracts any that
858 * need to be passed to the constructor and puts them into a paramList
859 * element
860 */
861 protected Element extractOtherParams(HashMap<String, Serializable> params, int type)
862 {
863 Document doc = XMLConverter.newDOM();
864 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
865 if (type == GS2PerlConstructor.NEW)
866 {
867 Element param = doc.createElement(GSXML.PARAM_ELEM);
868 param.setAttribute(GSXML.NAME_ATT, "creator");
869 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(CREATOR_PARAM));
870
871 param_list.appendChild(param);
872 param = doc.createElement(GSXML.PARAM_ELEM);
873 param.setAttribute(GSXML.NAME_ATT, "about");
874 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_ABOUT_PARAM));
875 param_list.appendChild(param);
876 param = doc.createElement(GSXML.PARAM_ELEM);
877 param.setAttribute(GSXML.NAME_ATT, "title");
878 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_TITLE_PARAM));
879 param_list.appendChild(param);
880 param = doc.createElement(GSXML.PARAM_ELEM);
881 param.setAttribute(GSXML.NAME_ATT, "buildtype");
882 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(BUILDTYPE_PARAM));
883 param_list.appendChild(param);
884 return param_list;
885 }
886
887 // other ones dont have params yet
888 return null;
889 }
890
891 protected void waitUntilReady(Element request)
892 {
893 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
894 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
895
896 String collection = (String) params.get(COL_PARAM);
897
898 if (checkCollectionIsNotBusy(collection))
899 {
900 return;
901 }
902
903 while (!checkCollectionIsNotBusy(collection)) // When the collection ceases to be busy, we place a hold on it
904 {
905 try
906 {
907 Thread.currentThread().sleep(1000);
908 }
909 catch (Exception ex)
910 {
911 ex.printStackTrace();
912 }
913 }
914 }
915
916 protected void signalReady(Element request)
917 {
918 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
919 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
920
921 String collection = (String) params.get(COL_PARAM);
922
923 collectionOperationMap.remove(collection);
924 }
925
926 // If collection is NOT busy, then reserve it
927 protected synchronized boolean checkCollectionIsNotBusy(String collection)
928 {
929 if (collectionOperationMap.get(collection) == null)
930 {
931 collectionOperationMap.put(collection, true);
932 return true;
933 }
934 return false;
935 }
936
937
938 /** Copy from DebugService.userHasEditPermissions
939 This function checks that the user is logged in and that the user
940 is in the right group to edit the collection */
941 protected boolean userHasCollectionEditPermissions(Element request) {
942 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
943 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
944 String collection = (String) params.get(COL_PARAM); // could be null on newcoll operation
945
946 UserContext context = new UserContext(request);
947 if(collection == null) {
948 return !context.getUsername().equals("");
949 }
950 for (String group : context.getGroups()) {
951 // administrator always has permission
952 if (group.equals("administrator")) {
953 return true;
954 }
955 // all-collections-editor can edit any collection
956 if (!collection.equals("")) {
957 if (group.equals("all-collections-editor")) {
958 return true;
959 }
960 if (group.equals(collection+"-collection-editor")) {
961 return true;
962 }
963 }
964 }
965 // haven't found a group with edit permissions
966 return false;
967
968 }
969 protected void markDocumentInFlatDatabase(String mark, String collection, String oid) {
970
971 Document msg_doc = XMLConverter.newDOM();
972 Element message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
973 UserContext userContext = new UserContext();
974 Element query_request = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_DESCRIBE , collection, userContext);
975 message.appendChild(query_request);
976 Element result = (Element) this.router.process(message);
977 Element resp_elem = (Element) GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
978 Element coll_elem = (Element) GSXML.getChildByTagName(resp_elem, GSXML.COLLECTION_ELEM);
979 String dbtype = coll_elem.getAttribute(GSXML.DB_TYPE_ATT);
980
981 SimpleCollectionDatabase coll_db = new SimpleCollectionDatabase(dbtype);
982 if (!coll_db.databaseOK())
983 {
984 logger.error("Couldn't create the collection database of type " + dbtype);
985 return;
986 }
987
988 // Open database for reading. It may not exist if collection is pre-built without archives (such as demo collections)
989 String coll_db_file = GSFile.archivesDatabaseFile(this.site_home, collection, dbtype);
990 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.READ))
991 {
992 logger.error("Could not open collection archives database. Database doesn't exist or else somebody's already using it?");
993 return;
994 }
995 // now we know we have an archives folder
996 String old_value = coll_db.getValue(oid);
997 String new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
998 // Close database for reading
999 coll_db.closeDatabase();
1000 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.WRITE))
1001 {
1002 logger.error("Could not open collection archives database. Somebody already using this database!");
1003 return;
1004 }
1005 coll_db.setValue(oid, new_value);
1006 coll_db.closeDatabase();
1007
1008 }
1009}
Note: See TracBrowser for help on using the repository browser.