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

Last change on this file since 31125 was 31125, checked in by ak19, 7 years ago

Experimental fix for concurrent access issue when using document editor to modify metadata. Need to commit now in order to test on other win machine where the race condition manifests in the binary. That Win machine doesn't have Vis Studio for compiling, and compiling just the core (Java) is insufficient as the tomcat won't even start after that perhaps because other parts were no compiled up.

  • Property svn:keywords set to Author Date Id Revision
File size: 36.5 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
321 // wait until we can reserve the collection for processing
322 waitUntilReady(request);
323
324 logger.error("@@@ RESERVED");
325
326 // process
327 Element response = runCommand(request, GS2PerlConstructor.MODIFY_METADATA_SERVER);
328
329 if (response.getElementsByTagName(GSXML.ERROR_ELEM).getLength() <= 0) // if no errors, wait for process to finish
330 {
331 logger.error("@@@ NO ERRORS");
332
333 Element statusElem = (Element) response.getElementsByTagName(GSXML.STATUS_ELEM).item(0);
334 String id = statusElem.getAttribute("pid");
335 logger.error("@@@ GOT PID: " + id);
336
337 GS2PerlListener currentListener = this.listeners.get(id);
338 int statusCode = currentListener.getStatus();
339 while (!GSStatus.isCompleted(statusCode))
340 {
341 // wait for the process, and keep checking the status code
342 // there is probably a better way to do this.
343 try
344 {
345 logger.error("@@@ WAITING");
346 Thread.currentThread().sleep(100);
347 }
348 catch (Exception e)
349 { // ignore
350 }
351 statusCode = currentListener.getStatus();
352 }
353 }
354
355 else {
356 logger.error("@@@ GOT ERROR");
357 }
358
359 logger.error("@@@ RELEASING HOLD");
360
361 // release hold on collection
362 signalReady(request);
363 return response;
364
365 }
366
367 protected Element processActivateCollection(Element request)
368 {
369
370 if (!userHasCollectionEditPermissions(request)) {
371 Document result_doc = XMLConverter.newDOM();
372 Element result = GSXML.createBasicResponse(result_doc, "processActivateCollection");
373 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
374 return result;
375 }
376
377 // this activates the collection on disk. but now we need to tell
378 // the MR about it. but we have to wait until the process is finished.
379 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
380 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
381 String coll_name = (String) params.get(COL_PARAM);
382 String lang = request.getAttribute(GSXML.LANG_ATT);
383
384 UserContext userContext = new UserContext(request);
385 String request_type = request.getAttribute(GSXML.TYPE_ATT);
386
387 // now we de-activate the collection before running activate.pl, and then re-activate at end
388 // So activate.pl only does the moving, no activation. This way will prevent java from launching
389 // perl, exiting and then leaving dangling file handles (on index/text/col.gdb) in perl.
390 if (!request_type.equals(GSXML.REQUEST_TYPE_STATUS)) {
391 systemRequest("delete", coll_name, null, userContext); // deactivate collection
392 }
393
394 Element response = runCommand(request, GS2PerlConstructor.ACTIVATE); // if request is for STATUS, then this won't run activate.pl
395
396 Element status = (Element) GSXML.getChildByTagName(response, GSXML.STATUS_ELEM);
397
398 // check for finished
399 int status_code = Integer.parseInt(status.getAttribute(GSXML.STATUS_ERROR_CODE_ATT));
400 if (GSStatus.isCompleted(status_code) && GSStatus.isError(status_code))
401 {
402 // we shouldn't carry out the next bit, just return the response
403 return response;
404 }
405 String id = status.getAttribute(GSXML.STATUS_PROCESS_ID_ATT);
406 GS2PerlListener listener = this.listeners.get(id);
407 if (listener == null)
408 {
409 logger.error("somethings gone wrong, couldn't find the listener");
410 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
411 return response;
412 }
413
414 while (!GSStatus.isCompleted(status_code))
415 {
416 // wait for the process, and keep checking the status code
417 // there is probably a better way to do this.
418 try
419 {
420 Thread.currentThread().sleep(100);
421 }
422 catch (Exception e)
423 { // ignore
424 }
425 status_code = listener.getStatus();
426 }
427
428 // add the rest of the messages to the status node
429 Text t = status.getOwnerDocument().createTextNode("\n" + listener.getUpdate());
430 status.appendChild(t);
431 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
432 if (GSStatus.isError(status_code))
433 {
434 return response; // without doing the next bit
435 }
436
437 t = status.getOwnerDocument().createTextNode("\n");
438 status.appendChild(t);
439 // once have got here, we assume
440 // the first bit proceeded successfully, now reload the collection (sends a collection reactivation request)
441 systemRequest("reload", coll_name, status, userContext); // this will append more messages to the status, and overwrite the error code att
442 return response;
443
444 }
445
446 protected Element processDeleteCollection(Element request)
447 {
448 if (!userHasCollectionEditPermissions(request)) {
449 Document result_doc = XMLConverter.newDOM();
450 Element result = GSXML.createBasicResponse(result_doc, "processDeleteCollection");
451 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
452 return result;
453 }
454
455 Document result_doc = XMLConverter.newDOM();
456 // the response to send back
457 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
458 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
459 response.setAttribute(GSXML.FROM_ATT, name);
460 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
461 response.appendChild(status);
462 Text t = null; // the text node for the error/success message
463 String lang = request.getAttribute(GSXML.LANG_ATT);
464 String request_type = request.getAttribute(GSXML.TYPE_ATT);
465
466 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
467 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
468
469 boolean get_status_only = false;
470 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
471 {
472 get_status_only = true;
473 }
474 if (get_status_only)
475 {
476 // 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
477 logger.error("had a status request for delete - this shouldn't happen!!");
478 //t = result_doc.createTextNode("");
479 //status.appendChild(t);
480 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
481 return response;
482 }
483 String coll_name = (String) params.get(COL_PARAM);
484 String[] args = { coll_name };
485 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, coll_name));
486 // check that the coll is there in the first place
487 if (!coll_dir.exists())
488 {
489 t = result_doc.createTextNode(getTextString("delete.exists_error", lang, args));
490 status.appendChild(t);
491 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
492 return response;
493 }
494
495 // try to delete the directory
496 if (!GSFile.deleteFile(coll_dir))
497 {
498 t = result_doc.createTextNode(getTextString("delete.delete_error", lang, args));
499 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
500 status.appendChild(t);
501 return response;
502 }
503
504 UserContext userContext = new UserContext(request);
505
506 systemRequest("delete", coll_name, status, userContext);
507 return response;
508 }
509
510 protected Element processReloadCollection(Element request)
511 {
512 if (!userHasCollectionEditPermissions(request)) {
513 Document result_doc = XMLConverter.newDOM();
514 Element result = GSXML.createBasicResponse(result_doc, "processReloadCollection");
515 GSXML.addError(result, "This user does not have the required permissions to perform this action.");
516 return result;
517 }
518
519 Document result_doc = XMLConverter.newDOM();
520 // the response to send back
521 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
522 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
523 response.setAttribute(GSXML.FROM_ATT, name);
524 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
525 response.appendChild(status);
526 Text t = null; // the text node for the error/success message
527
528 String lang = request.getAttribute(GSXML.LANG_ATT);
529 String request_type = request.getAttribute(GSXML.TYPE_ATT);
530
531 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
532 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
533
534 boolean get_status_only = false;
535 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
536 {
537 get_status_only = true;
538 }
539 if (get_status_only)
540 {
541 // reload is synchronous - this makes no sense
542 logger.error("had a status request for reload - this shouldn't happen!!");
543 //t = result_doc.createTextNode("");
544 //status.appendChild(t);
545 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
546 return response;
547 }
548
549 String coll_name = (String) params.get(COL_PARAM);
550
551 UserContext userContext = new UserContext(request);
552
553 systemRequest("reload", coll_name, status, userContext);
554 return response;
555
556 }
557
558 /**
559 * send a configure request to the message router action name should be
560 * "delete" or "reload" response will be put into the status element
561 */
562 protected void systemRequest(String operation, String coll_name, Element status, UserContext userContext)
563 {
564 // send the request to the MR
565 Document doc = XMLConverter.newDOM();
566 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
567 Element request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_SYSTEM, "", userContext);
568 message.appendChild(request);
569 Element command = doc.createElement(GSXML.SYSTEM_ELEM);
570 request.appendChild(command);
571 command.setAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT, GSXML.COLLECTION_ELEM);
572 command.setAttribute(GSXML.SYSTEM_MODULE_NAME_ATT, coll_name);
573
574 if (operation.equals("delete"))
575 {
576 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_DEACTIVATE);
577 }
578 else if (operation.equals("reload"))
579 {
580 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_ACTIVATE);
581 }
582 else
583 {
584 logger.error("invalid action name passed to systemRequest:" + operation);
585 return;
586 }
587 request.appendChild(command);
588 Node response = this.router.process(message); // at the moment, get no info in response so ignore it
589 Text t;
590 String[] args = { coll_name };
591
592 if (status != null)
593 {
594 if (response == null)
595 {
596 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".configure_error", userContext.getLanguage(), args));
597 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
598 status.appendChild(t);
599 return;
600 }
601
602 // if we got here, we have succeeded!
603 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".success", userContext.getLanguage(), args));
604 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.SUCCESS));
605 status.appendChild(t);
606 }
607 }
608
609 /**
610 * configure the service module for now, all services have type=build - need
611 * to think about this
612 */
613 public boolean configure(Element info, Element extra_info)
614 {
615 if (!super.configure(info, extra_info))
616 {
617 return false;
618 }
619
620 logger.info("configuring GS2Construct");
621
622 Element e = null;
623 // hard code in the services for now
624
625 // set up short_service_info_ - for now just has name and type
626
627 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
628 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
629 e.setAttribute(GSXML.NAME_ATT, NEW_SERVICE);
630 this.short_service_info.appendChild(e);
631
632 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
633 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
634 e.setAttribute(GSXML.NAME_ATT, IMPORT_SERVICE);
635 this.short_service_info.appendChild(e);
636
637 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
638 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
639 e.setAttribute(GSXML.NAME_ATT, BUILD_SERVICE);
640 this.short_service_info.appendChild(e);
641
642 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
643 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
644 e.setAttribute(GSXML.NAME_ATT, ACTIVATE_SERVICE);
645 this.short_service_info.appendChild(e);
646
647 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
648 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
649 e.setAttribute(GSXML.NAME_ATT, BUILD_AND_ACTIVATE_SERVICE);
650 this.short_service_info.appendChild(e);
651
652 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
653 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
654 e.setAttribute(GSXML.NAME_ATT, DELETE_SERVICE);
655 this.short_service_info.appendChild(e);
656
657 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
658 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
659 e.setAttribute(GSXML.NAME_ATT, RELOAD_SERVICE);
660 this.short_service_info.appendChild(e);
661
662 //e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
663 //e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
664 //e.setAttribute(GSXML.NAME_ATT, ADD_DOC_SERVICE);
665 //this.short_service_info.appendChild(e);
666
667 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
668 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
669 e.setAttribute(GSXML.NAME_ATT, MODIFY_METADATA_SERVICE);
670 this.short_service_info.appendChild(e);
671
672 return true;
673 }
674
675 /** returns a response element */
676 protected Element runCommand(Element request, int type)
677 {
678 Document result_doc = XMLConverter.newDOM();
679 // the response to send back
680 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
681 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
682 response.setAttribute(GSXML.FROM_ATT, name);
683 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
684 response.appendChild(status);
685
686 String lang = request.getAttribute(GSXML.LANG_ATT);
687 String request_type = request.getAttribute(GSXML.TYPE_ATT);
688
689 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
690 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
691
692 boolean get_status_only = false;
693 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
694 {
695 get_status_only = true;
696 }
697
698 // just check for status messages if that's all that's required
699 if (get_status_only)
700 {
701 String id = (String) params.get(PROCESS_ID_PARAM);
702 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
703 GS2PerlListener listener = this.listeners.get(id);
704 if (listener == null)
705 {
706 Text t = result_doc.createTextNode(getTextString("general.process_id_error", lang));
707 status.appendChild(t);
708 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
709 }
710 else
711 {
712 Text t = result_doc.createTextNode(listener.getUpdate());
713 status.appendChild(t);
714 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
715 // check that we actually should be removing the listener here
716 if (listener.isFinished())
717 { // remove this listener - its job is done
718 this.listeners.remove(id); // not working
719 }
720 }
721 return response;
722
723 }
724
725 // do the actual command
726 String coll_name = null;
727 if (type == GS2PerlConstructor.NEW)
728 {
729 String coll_title = (String) params.get(NEW_COL_TITLE_PARAM);
730 coll_name = createNewCollName(coll_title);
731 }
732 else
733 {
734 coll_name = (String) params.get(COL_PARAM);
735 }
736
737 // makes a paramList of the relevant params
738 Element other_params = extractOtherParams(params, type);
739
740 //create the constructor to do the work
741 GS2PerlConstructor constructor = new GS2PerlConstructor("perl_build");
742 if (!constructor.configure())
743 {
744 Text t = result_doc.createTextNode(getTextString("general.configure_constructor_error", lang));
745 status.appendChild(t);
746 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
747 return response;
748 }
749
750 constructor.setSiteHome(this.site_home);
751 constructor.setCollectionName(coll_name);
752 constructor.setActionType(type);
753 constructor.setProcessParams(other_params);
754 if (type == GS2PerlConstructor.IMPORT)
755 {
756 constructor.setManifestFile(this.site_home + File.separator + "collect" + File.separator + params.get(COL_PARAM) + File.separator + "manifests" + File.separator + "tempManifest.xml");
757 }
758 else if (type == GS2PerlConstructor.MODIFY_METADATA_SERVER) {
759 StringBuffer querystring = new StringBuffer();
760
761 // convert params into a single string again?
762 Set<Map.Entry<String, Serializable>> entries = params.entrySet();
763 Iterator<Map.Entry<String, Serializable>> i = entries.iterator();
764
765 String oid = null;
766
767 while (i.hasNext()) {
768
769 Map.Entry<String, Serializable> entry = i.next();
770 String paramname = entry.getKey();
771 paramname = paramname.replace("s1.", ""); // replaces all
772 // occurrences
773 if (paramname.equals("collection")) {
774 paramname = "c";
775 }
776 if (paramname.equals("d")){
777 oid = (String) entry.getValue();
778 }
779 String paramvalue = (String) entry.getValue();
780
781 querystring.append(paramname + "=" + paramvalue);
782 if (i.hasNext()) {
783 querystring.append("&");
784 }
785 }
786
787 markDocumentInFlatDatabase("R", coll_name, OID.getTop(oid));
788
789 constructor.setQueryString(querystring.toString());
790 }
791
792 GS2PerlListener listener = new GS2PerlListener();
793 constructor.addListener(listener);
794 constructor.start();
795
796 String id = newID();
797 this.listeners.put(id, listener);
798
799 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
800 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ACCEPTED));
801 Text t = result_doc.createTextNode(getTextString("general.process_start", lang));
802 status.appendChild(t);
803 return response;
804 }
805
806 //************************
807 // some helper functions
808 //************************
809
810 /** parse the collect directory and return a list of collection names */
811 protected String[] getCollectionList()
812 {
813
814 File collectDir = new File(GSFile.collectDir(this.site_home));
815 if (!collectDir.exists())
816 {
817 logger.error("couldn't find collect dir: " + collectDir.toString());
818 return null;
819 }
820 logger.info("GS2Construct: reading thru directory " + collectDir.getPath() + " to find collections.");
821 File[] contents = collectDir.listFiles();
822 int num_colls = 0;
823 for (int i = 0; i < contents.length; i++)
824 {
825 if (contents[i].isDirectory() && !contents[i].getName().startsWith("CVS"))
826 {
827 num_colls++;
828 }
829 }
830
831 String[] names = new String[num_colls];
832
833 for (int i = 0, j = 0; i < contents.length; i++)
834 {
835 if (contents[i].isDirectory())
836 {
837 String colName = contents[i].getName();
838 if (!colName.startsWith("CVS"))
839 {
840 names[j] = colName;
841 j++;
842 }
843
844 }
845 }
846
847 return names;
848
849 }
850
851 /** ids used for process id */
852 private int current_id = 0;
853
854 private String newID()
855 {
856 current_id++;
857 return Integer.toString(current_id);
858 }
859
860 /** creates a new short name from the collection title */
861 protected String createNewCollName(String coll_title)
862 {
863
864 String base_name = null;
865 // take the first 6 letters
866 if (coll_title.length() < 6)
867 {
868 base_name = coll_title;
869 }
870 else
871 {
872 base_name = coll_title.substring(0, 6);
873 }
874 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name));
875 if (!coll_dir.exists())
876 { // this name is ok - not used yet
877 return base_name;
878 }
879
880 // now we have to make a new name until we get a good one
881 // try name1, name2 name3 etc
882 int i = 0;
883 while (coll_dir.exists())
884 {
885 i++;
886 coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name + Integer.toString(i)));
887 }
888 return base_name + Integer.toString(i);
889
890 }
891
892 /**
893 * takes the params from the request (in the HashMap) and extracts any that
894 * need to be passed to the constructor and puts them into a paramList
895 * element
896 */
897 protected Element extractOtherParams(HashMap<String, Serializable> params, int type)
898 {
899 Document doc = XMLConverter.newDOM();
900 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
901 if (type == GS2PerlConstructor.NEW)
902 {
903 Element param = doc.createElement(GSXML.PARAM_ELEM);
904 param.setAttribute(GSXML.NAME_ATT, "creator");
905 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(CREATOR_PARAM));
906
907 param_list.appendChild(param);
908 param = doc.createElement(GSXML.PARAM_ELEM);
909 param.setAttribute(GSXML.NAME_ATT, "about");
910 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_ABOUT_PARAM));
911 param_list.appendChild(param);
912 param = doc.createElement(GSXML.PARAM_ELEM);
913 param.setAttribute(GSXML.NAME_ATT, "title");
914 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_TITLE_PARAM));
915 param_list.appendChild(param);
916 param = doc.createElement(GSXML.PARAM_ELEM);
917 param.setAttribute(GSXML.NAME_ATT, "buildtype");
918 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(BUILDTYPE_PARAM));
919 param_list.appendChild(param);
920 return param_list;
921 }
922
923 // other ones dont have params yet
924 return null;
925 }
926
927 protected void waitUntilReady(Element request)
928 {
929 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
930 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
931
932 String collection = (String) params.get(COL_PARAM);
933
934 if (checkCollectionIsNotBusy(collection))
935 {
936 return;
937 }
938
939 while (!checkCollectionIsNotBusy(collection)) // When the collection ceases to be busy, we place a hold on it
940 {
941 try
942 {
943 Thread.currentThread().sleep(1000);
944 }
945 catch (Exception ex)
946 {
947 ex.printStackTrace();
948 }
949 }
950 }
951
952 protected void signalReady(Element request)
953 {
954 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
955 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
956
957 String collection = (String) params.get(COL_PARAM);
958
959 collectionOperationMap.remove(collection);
960 }
961
962 // If collection is NOT busy, then reserve it
963 protected synchronized boolean checkCollectionIsNotBusy(String collection)
964 {
965 if (collectionOperationMap.get(collection) == null)
966 {
967 collectionOperationMap.put(collection, true);
968 return true;
969 }
970 return false;
971 }
972
973
974 /** Copy from DebugService.userHasEditPermissions
975 This function checks that the user is logged in and that the user
976 is in the right group to edit the collection */
977 protected boolean userHasCollectionEditPermissions(Element request) {
978 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
979 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
980 String collection = (String) params.get(COL_PARAM); // could be null on newcoll operation
981
982 UserContext context = new UserContext(request);
983 if(collection == null) {
984 return !context.getUsername().equals("");
985 }
986 for (String group : context.getGroups()) {
987 // administrator always has permission
988 if (group.equals("administrator")) {
989 return true;
990 }
991 // all-collections-editor can edit any collection
992 if (!collection.equals("")) {
993 if (group.equals("all-collections-editor")) {
994 return true;
995 }
996 if (group.equals(collection+"-collection-editor")) {
997 return true;
998 }
999 }
1000 }
1001 // haven't found a group with edit permissions
1002 return false;
1003
1004 }
1005 protected void markDocumentInFlatDatabase(String mark, String collection, String oid) {
1006
1007 Document msg_doc = XMLConverter.newDOM();
1008 Element message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1009 UserContext userContext = new UserContext();
1010 Element query_request = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_DESCRIBE , collection, userContext);
1011 message.appendChild(query_request);
1012 Element result = (Element) this.router.process(message);
1013 Element resp_elem = (Element) GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
1014 Element coll_elem = (Element) GSXML.getChildByTagName(resp_elem, GSXML.COLLECTION_ELEM);
1015 String dbtype = coll_elem.getAttribute(GSXML.DB_TYPE_ATT);
1016
1017 SimpleCollectionDatabase coll_db = new SimpleCollectionDatabase(dbtype);
1018 if (!coll_db.databaseOK())
1019 {
1020 logger.error("Couldn't create the collection database of type " + dbtype);
1021 return;
1022 }
1023
1024 // Open database for reading. It may not exist if collection is pre-built without archives (such as demo collections)
1025 String coll_db_file = GSFile.archivesDatabaseFile(this.site_home, collection, dbtype);
1026 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.READ))
1027 {
1028 logger.error("Could not open collection archives database. Database doesn't exist or else somebody's already using it?");
1029 return;
1030 }
1031 // now we know we have an archives folder
1032 String old_value = coll_db.getValue(oid);
1033 String new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
1034 // Close database for reading
1035 coll_db.closeDatabase();
1036 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.WRITE))
1037 {
1038 logger.error("Could not open collection archives database. Somebody already using this database!");
1039 return;
1040 }
1041 coll_db.setValue(oid, new_value);
1042 coll_db.closeDatabase();
1043
1044 }
1045}
Note: See TracBrowser for help on using the repository browser.