Revision 2031
Added by Duane Costa about 20 years ago
HarvestSiteSchedule.java | ||
---|---|---|
6 | 6 |
|
7 | 7 |
package edu.ucsb.nceas.metacat.harvesterClient; |
8 | 8 |
|
9 |
import java.io.*; |
|
9 |
import java.io.FileNotFoundException; |
|
10 |
import java.io.IOException; |
|
11 |
import java.io.InputStream; |
|
12 |
import java.io.InputStreamReader; |
|
13 |
import java.io.Reader; |
|
14 |
import java.net.MalformedURLException; |
|
15 |
import java.net.URL; |
|
10 | 16 |
import java.sql.Connection; |
11 | 17 |
import java.sql.SQLException; |
12 | 18 |
import java.sql.Statement; |
13 |
import java.text.*; |
|
14 |
import java.util.*; |
|
15 |
import javax.xml.parsers.*; |
|
16 |
import org.xml.sax.*; |
|
17 |
import org.xml.sax.helpers.*; |
|
19 |
import java.text.DateFormat; |
|
20 |
import java.text.ParseException; |
|
21 |
import java.text.SimpleDateFormat; |
|
22 |
import java.util.ArrayList; |
|
23 |
import java.util.Date; |
|
24 |
import javax.xml.parsers.ParserConfigurationException; |
|
25 |
import org.xml.sax.Attributes; |
|
26 |
import org.xml.sax.ContentHandler; |
|
27 |
import org.xml.sax.ErrorHandler; |
|
28 |
import org.xml.sax.InputSource; |
|
29 |
import org.xml.sax.SAXException; |
|
30 |
import org.xml.sax.SAXParseException; |
|
31 |
import org.xml.sax.XMLReader; |
|
32 |
import org.xml.sax.helpers.DefaultHandler; |
|
33 |
import org.xml.sax.helpers.XMLReaderFactory; |
|
18 | 34 |
|
19 |
import edu.ucsb.nceas.metacat.client.*; |
|
35 |
import edu.ucsb.nceas.metacat.client.Metacat; |
|
36 |
import edu.ucsb.nceas.metacat.client.MetacatException; |
|
37 |
import edu.ucsb.nceas.metacat.client.MetacatInaccessibleException; |
|
20 | 38 |
|
21 | 39 |
|
22 | 40 |
/** |
... | ... | |
33 | 51 |
private long delta; |
34 | 52 |
private String documentListURL; |
35 | 53 |
private Harvester harvester; |
36 |
private int harvestDocumentIndex = 0; |
|
37 |
private HarvestDocument[] harvestDocumentList = new HarvestDocument[30]; |
|
54 |
private ArrayList harvestDocumentList = new ArrayList(); |
|
38 | 55 |
private String harvestSiteEndTime; |
39 | 56 |
private String harvestSiteStartTime; |
40 | 57 |
private String ldapDN; |
41 |
private String ldapPassword;
|
|
58 |
private String ldapPwd;
|
|
42 | 59 |
final private long millisecondsPerDay = (1000 * 60 * 60 * 24); |
43 |
private int siteScheduleID;
|
|
60 |
int siteScheduleID; |
|
44 | 61 |
private String unit; |
45 | 62 |
private int updateFrequency; |
46 | 63 |
|
... | ... | |
52 | 69 |
* @param siteScheduleID the value of the SITE_SCHEDULE_ID field |
53 | 70 |
* @param documentListURL the value of the DOCUMENTLISTURL field |
54 | 71 |
* @param ldapDN the value of the LDAPDN field |
55 |
* @param ldapPassword the value of the LDAPPASSWORD field
|
|
72 |
* @param ldapPwd the value of the LDAPPASSWORD field
|
|
56 | 73 |
* @param dateNextHarvest the value of the DATENEXTHARVEST field |
57 | 74 |
* @param dateLastHarvest the value of the DATELASTHARVEST field |
58 | 75 |
* @param updateFrequency the value of the UPDATEFREQUENCY field |
... | ... | |
64 | 81 |
int siteScheduleID, |
65 | 82 |
String documentListURL, |
66 | 83 |
String ldapDN, |
67 |
String ldapPassword,
|
|
84 |
String ldapPwd,
|
|
68 | 85 |
String dateNextHarvest, |
69 | 86 |
String dateLastHarvest, |
70 | 87 |
int updateFrequency, |
... | ... | |
76 | 93 |
this.siteScheduleID = siteScheduleID; |
77 | 94 |
this.documentListURL = documentListURL; |
78 | 95 |
this.ldapDN = ldapDN; |
79 |
this.ldapPassword = ldapPassword;
|
|
96 |
this.ldapPwd = ldapPwd;
|
|
80 | 97 |
this.dateNextHarvest = dateNextHarvest; |
81 | 98 |
this.dateLastHarvest = dateLastHarvest; |
82 | 99 |
this.updateFrequency = updateFrequency; |
... | ... | |
102 | 119 |
* harvest based on today's date and the update frequency. |
103 | 120 |
*/ |
104 | 121 |
private void dbUpdateHarvestSiteSchedule() { |
105 |
Connection con; |
|
122 |
Connection conn;
|
|
106 | 123 |
long currentTime; // Current time in milliseconds |
107 | 124 |
Date dateNextHarvest; // Date of next harvest |
108 | 125 |
String lastHarvest; |
... | ... | |
112 | 129 |
Statement stmt; |
113 | 130 |
long timeNextHarvest; |
114 | 131 |
|
115 |
con = harvester.conn; |
|
132 |
conn = harvester.conn;
|
|
116 | 133 |
now = new Date(); |
117 |
currentTime = now.getTime(); // Current time in milliseconds
|
|
134 |
currentTime = now.getTime(); |
|
118 | 135 |
timeNextHarvest = currentTime + delta; |
119 | 136 |
dateNextHarvest = new Date(timeNextHarvest); |
120 | 137 |
nextHarvest = "'" + simpleDateFormat.format(dateNextHarvest) + "'"; |
121 | 138 |
lastHarvest = "'" + simpleDateFormat.format(now) + "'"; |
122 | 139 |
|
123 |
System.out.println("Date of next harvest: " + nextHarvest); |
|
124 |
System.out.println("Date of last harvest: " + lastHarvest); |
|
125 |
|
|
126 | 140 |
try { |
127 |
stmt = con.createStatement(); |
|
128 |
stmt.executeUpdate("UPDATE HARVEST_SITE_SCHEDULE SET DATENEXTHARVEST = " + nextHarvest + " WHERE SITE_SCHEDULE_ID = " + siteScheduleID); |
|
129 |
stmt.executeUpdate("UPDATE HARVEST_SITE_SCHEDULE SET DATELASTHARVEST = " + lastHarvest + " WHERE SITE_SCHEDULE_ID = " + siteScheduleID); |
|
141 |
stmt = conn.createStatement(); |
|
142 |
stmt.executeUpdate("UPDATE HARVEST_SITE_SCHEDULE SET DATENEXTHARVEST = " + |
|
143 |
nextHarvest + |
|
144 |
" WHERE SITE_SCHEDULE_ID = " + |
|
145 |
siteScheduleID); |
|
146 |
stmt.executeUpdate("UPDATE HARVEST_SITE_SCHEDULE SET DATELASTHARVEST = " + |
|
147 |
lastHarvest + |
|
148 |
" WHERE SITE_SCHEDULE_ID = " + |
|
149 |
siteScheduleID); |
|
130 | 150 |
stmt.close(); |
131 | 151 |
} |
132 | 152 |
catch(SQLException e) { |
133 |
System.err.println("SQLException: " + e.getMessage());
|
|
153 |
System.out.println("SQLException: " + e.getMessage());
|
|
134 | 154 |
} |
135 | 155 |
} |
136 | 156 |
|
... | ... | |
157 | 177 |
|
158 | 178 |
if (timeNextHarvest < currentTime) { |
159 | 179 |
dueForHarvest = true; |
180 |
System.out.println("Due for harvest: " + documentListURL); |
|
160 | 181 |
} |
161 | 182 |
else { |
162 |
System.out.println("Next harvest date: " + dnh.toString());
|
|
183 |
System.out.println("Not due for harvest: " + documentListURL);
|
|
163 | 184 |
} |
164 | 185 |
} |
165 | 186 |
catch (ParseException e) { |
166 |
System.err.println("Error parsing date: " + e.getMessage());
|
|
187 |
System.out.println("Error parsing date: " + e.getMessage());
|
|
167 | 188 |
} |
168 | 189 |
|
169 |
//return dueForHarvest; |
|
170 |
return true; |
|
190 |
return dueForHarvest; |
|
171 | 191 |
} |
172 | 192 |
|
173 | 193 |
|
... | ... | |
186 | 206 |
parseDocumentList(); |
187 | 207 |
metacatLogin(); |
188 | 208 |
|
189 |
for (int i = 0; i < harvestDocumentList.length; i++) {
|
|
190 |
harvestDocument = harvestDocumentList[i];
|
|
209 |
for (int i = 0; i < harvestDocumentList.size(); i++) {
|
|
210 |
harvestDocument = (HarvestDocument) harvestDocumentList.get(i);
|
|
191 | 211 |
|
192 | 212 |
if (harvestDocument != null) { |
193 |
harvestDocument.printOutput(); |
|
194 | 213 |
harvestDocument.harvestDocument(); |
195 | 214 |
} |
196 | 215 |
} |
... | ... | |
199 | 218 |
dbUpdateHarvestSiteSchedule(); |
200 | 219 |
} |
201 | 220 |
catch (ParserConfigurationException e) { |
202 |
System.err.println("ParserConfigurationException: " + e.getMessage());
|
|
221 |
System.out.println("ParserConfigurationException: " + e.getMessage());
|
|
203 | 222 |
} |
204 |
catch (SAXException e) { |
|
205 |
System.err.println("SAXException: " + e.getMessage()); |
|
206 |
} |
|
207 |
catch (IOException e) { |
|
208 |
System.err.println("IOException: " + e.getMessage()); |
|
209 |
} |
|
210 | 223 |
|
211 | 224 |
reportToSite(); |
212 | 225 |
} |
... | ... | |
214 | 227 |
|
215 | 228 |
|
216 | 229 |
/** |
217 |
* Login to Metacat using the ldapDN and ldapPassword
|
|
230 |
* Login to Metacat using the ldapDN and ldapPwd
|
|
218 | 231 |
*/ |
219 | 232 |
private void metacatLogin() { |
220 | 233 |
Metacat metacat = harvester.metacat; |
221 | 234 |
|
222 | 235 |
if (harvester.connectToMetacat()) { |
223 |
|
|
224 | 236 |
try { |
225 | 237 |
System.out.println("Logging in to Metacat: " + ldapDN); |
226 |
metacat.login(ldapDN, ldapPassword);
|
|
238 |
metacat.login(ldapDN, ldapPwd);
|
|
227 | 239 |
//System.out.println("Metacat login response: " + response); |
228 | 240 |
//sessionId = metacat.getSessionId(); |
229 | 241 |
//System.out.println("Session ID: " + sessionId); |
... | ... | |
234 | 246 |
catch (Exception e) { |
235 | 247 |
System.out.println("Metacat login failed." + e.getMessage()); |
236 | 248 |
} |
237 |
} |
|
238 |
else { |
|
239 |
System.out.println("Not logging in to Metacat"); |
|
240 |
} |
|
241 |
|
|
249 |
} |
|
242 | 250 |
} |
243 | 251 |
|
244 | 252 |
|
... | ... | |
261 | 269 |
System.out.println("Metacat exception: " + e.getMessage()); |
262 | 270 |
} |
263 | 271 |
} |
264 |
else { |
|
265 |
System.out.println("Not logging out from Metacat"); |
|
266 |
} |
|
267 | 272 |
} |
268 | 273 |
|
269 | 274 |
|
270 | 275 |
/** |
271 | 276 |
* Parse the site document list to find out which documents to harvest. |
272 |
* |
|
273 |
* @throws SAXException |
|
274 |
* @throws IOException |
|
275 |
* @throws ParserConfigurationException |
|
276 | 277 |
*/ |
277 | 278 |
private void parseDocumentList() |
278 |
throws SAXException, IOException, ParserConfigurationException { |
|
279 |
|
|
280 |
// Create a parser factory and use it to create a parser |
|
281 |
SAXParserFactory parserFactory = SAXParserFactory.newInstance(); |
|
282 |
SAXParser parser = parserFactory.newSAXParser(); |
|
283 |
|
|
284 |
// Instantiate a DefaultHandler subclass to do your counting for you |
|
285 |
DocumentListHandler handler = new DocumentListHandler(); |
|
286 |
|
|
287 |
// Start the parser. It reads the document list and calls methods of the handler. |
|
288 |
parser.parse(documentListURL, handler); |
|
279 |
throws ParserConfigurationException { |
|
280 |
DocumentListHandler documentListHandler = new DocumentListHandler(); |
|
281 |
InputStream inputStream; |
|
282 |
InputStreamReader inputStreamReader; |
|
283 |
String schemaLocation = "."; |
|
284 |
URL url; |
|
285 |
|
|
286 |
try { |
|
287 |
url = new URL(documentListURL); |
|
288 |
inputStream = url.openStream(); |
|
289 |
harvester.addLogEntry(0, "", "GetDocListSuccess", |
|
290 |
siteScheduleID, null, ""); |
|
291 |
inputStreamReader = new InputStreamReader(inputStream); |
|
292 |
documentListHandler.runParser(inputStreamReader, schemaLocation); |
|
293 |
harvester.addLogEntry(0, "", "ValidateDocListSuccess", |
|
294 |
siteScheduleID, null, ""); |
|
295 |
} |
|
296 |
catch (MalformedURLException e){ |
|
297 |
harvester.addLogEntry(1, "MalformedURLException: " + e.getMessage(), |
|
298 |
"GetDocListError", siteScheduleID, null, ""); |
|
299 |
} |
|
300 |
catch (FileNotFoundException e) { |
|
301 |
harvester.addLogEntry(1, "FileNotFoundException: " + e.getMessage(), |
|
302 |
"GetDocListError", siteScheduleID, null, ""); |
|
303 |
} |
|
304 |
catch (SAXException e) { |
|
305 |
harvester.addLogEntry(1, "SAXException: " + e.getMessage(), |
|
306 |
"ValidateDocListError", siteScheduleID, null, ""); |
|
307 |
} |
|
308 |
catch (ClassNotFoundException e) { |
|
309 |
harvester.addLogEntry(1, "ClassNotFoundException: " + e.getMessage(), |
|
310 |
"ValidateDocListError", siteScheduleID, null, ""); |
|
311 |
} |
|
312 |
catch (IOException e) { |
|
313 |
harvester.addLogEntry(1, "IOException: " + e.getMessage(), |
|
314 |
"GetDocListError", siteScheduleID, null, ""); |
|
315 |
} |
|
289 | 316 |
} |
290 | 317 |
|
291 | 318 |
|
... | ... | |
293 | 320 |
* Prints the data that is stored in this HarvestSiteSchedule object. |
294 | 321 |
*/ |
295 | 322 |
void printOutput() { |
296 |
System.out.println(""); |
|
297 |
System.out.println("siteScheduleID: " + siteScheduleID); |
|
298 |
System.out.println("documentListURL: " + documentListURL); |
|
299 |
System.out.println("ldapDN: " + ldapDN); |
|
300 |
System.out.println("ldapPassword: " + ldapPassword); |
|
301 |
System.out.println("dateNextHarvest: " + dateNextHarvest); |
|
302 |
System.out.println("dateLastHarvest: " + dateLastHarvest); |
|
303 |
System.out.println("updateFrequency: " + updateFrequency); |
|
304 |
System.out.println("unit: " + unit); |
|
305 |
System.out.println("contactEmail: " + contactEmail); |
|
323 |
System.out.println("siteScheduleID: " + siteScheduleID); |
|
324 |
System.out.println("documentListURL: " + documentListURL); |
|
325 |
System.out.println("ldapDN: " + ldapDN); |
|
326 |
System.out.println("dateNextHarvest: " + dateNextHarvest); |
|
327 |
System.out.println("dateLastHarvest: " + dateLastHarvest); |
|
328 |
System.out.println("updateFrequency: " + updateFrequency); |
|
329 |
System.out.println("unit: " + unit); |
|
330 |
System.out.println("contactEmail: " + contactEmail); |
|
306 | 331 |
} |
307 | 332 |
|
308 | 333 |
|
309 | 334 |
/** |
310 |
* Pushes a HarvestDocument object onto the harvestDocumentList. |
|
311 |
* |
|
312 |
* @param harvestDocument a new HarvestDocument object to add to the list |
|
313 |
*/ |
|
314 |
void pushHarvestDocument(HarvestDocument harvestDocument) { |
|
315 |
harvestDocumentList[harvestDocumentIndex] = harvestDocument; |
|
316 |
harvestDocumentIndex++; |
|
317 |
} |
|
318 |
|
|
319 |
|
|
320 |
/** |
|
321 | 335 |
* Sends a report to the site summarizing the results of the harvest |
322 | 336 |
* operation. |
323 | 337 |
*/ |
324 | 338 |
void reportToSite() { |
325 |
System.out.println("Sending report to site.\n");
|
|
339 |
System.out.println("Sending report to site: " + contactEmail);
|
|
326 | 340 |
} |
327 | 341 |
|
328 | 342 |
|
... | ... | |
331 | 345 |
* creating a new HarvestDocument object every time it finds a </Document> |
332 | 346 |
* end tag. |
333 | 347 |
*/ |
334 |
class DocumentListHandler extends DefaultHandler { |
|
348 |
class DocumentListHandler extends DefaultHandler implements ErrorHandler {
|
|
335 | 349 |
|
336 | 350 |
public String scope; |
337 | 351 |
public int identifier; |
... | ... | |
339 | 353 |
public String documentType; |
340 | 354 |
public String documentURL; |
341 | 355 |
private String currentQname; |
356 |
public final static String DEFAULT_PARSER = |
|
357 |
"org.apache.xerces.parsers.SAXParser"; |
|
358 |
private boolean schemaValidate = true; |
|
342 | 359 |
|
343 | 360 |
|
344 |
/** |
|
345 |
* Handles a start-of-document event. |
|
361 |
/** |
|
362 |
* This method is called for any plain text within an element. |
|
363 |
* It parses the value for any of the following elements: |
|
364 |
* <scope>, <identifier>, <revision>, <documentType>, <documentURL> |
|
365 |
* |
|
366 |
* @param ch the character array holding the parsed text |
|
367 |
* @param start the start index |
|
368 |
* @param length the text length |
|
369 |
* |
|
346 | 370 |
*/ |
347 |
public void startDocument () { |
|
348 |
System.out.println("Started parsing " + documentListURL); |
|
371 |
public void characters (char ch[], int start, int length) { |
|
372 |
String s = new String(ch, start, length); |
|
373 |
|
|
374 |
if (length > 0) { |
|
375 |
if (currentQname.equals("scope")) { |
|
376 |
scope = s; |
|
377 |
} |
|
378 |
else if (currentQname.equals("identifier")) { |
|
379 |
identifier = Integer.parseInt(s); |
|
380 |
} |
|
381 |
else if (currentQname.equals("revision")) { |
|
382 |
revision = Integer.parseInt(s); |
|
383 |
} |
|
384 |
else if (currentQname.equals("documentType")) { |
|
385 |
documentType = s; |
|
386 |
} |
|
387 |
else if (currentQname.equals("documentURL")) { |
|
388 |
documentURL = s; |
|
389 |
} |
|
390 |
|
|
391 |
currentQname = ""; |
|
392 |
} |
|
349 | 393 |
} |
350 | 394 |
|
351 | 395 |
|
... | ... | |
358 | 402 |
|
359 | 403 |
|
360 | 404 |
/** |
361 |
* Handles a start-of-element event. |
|
362 |
* |
|
363 |
* @param uri |
|
364 |
* @param localname |
|
365 |
* @param qname |
|
366 |
* @param attributes |
|
367 |
*/ |
|
368 |
public void startElement(String uri, |
|
369 |
String localname, |
|
370 |
String qname, |
|
371 |
Attributes attributes) { |
|
372 |
|
|
373 |
currentQname = qname; |
|
374 |
} |
|
375 |
|
|
376 |
|
|
377 |
/** |
|
378 | 405 |
* Handles an end-of-element event. If the end tag is </Document>, then |
379 | 406 |
* creates a new HarvestDocument object and pushes it to the document |
380 | 407 |
* list. |
... | ... | |
399 | 426 |
documentType, |
400 | 427 |
documentURL |
401 | 428 |
); |
402 |
pushHarvestDocument(harvestDocument);
|
|
429 |
harvestDocumentList.add(harvestDocument);
|
|
403 | 430 |
} |
404 | 431 |
} |
405 | 432 |
|
406 | 433 |
|
407 |
/** |
|
408 |
* This method is called for any plain text within an element. |
|
409 |
* It parses the value for any of the following elements: |
|
410 |
* <scope>, <identifier>, <revision>, <documentType>, <documentURL> |
|
411 |
* |
|
412 |
* @param ch the character array holding the parsed text |
|
413 |
* @param start the start index |
|
414 |
* @param length the text length |
|
415 |
* |
|
434 |
/** |
|
435 |
* Method for handling errors during a parse |
|
436 |
* |
|
437 |
* @param exception The parsing error |
|
438 |
* @exception SAXException Description of Exception |
|
416 | 439 |
*/ |
417 |
public void characters (char ch[], int start, int length) { |
|
418 |
String s = new String(ch, start, length); |
|
419 |
|
|
420 |
if (length > 0) { |
|
421 |
if (currentQname.equals("scope")) { |
|
422 |
scope = s; |
|
423 |
} |
|
424 |
else if (currentQname.equals("identifier")) { |
|
425 |
identifier = Integer.parseInt(s); |
|
426 |
} |
|
427 |
else if (currentQname.equals("revision")) { |
|
428 |
revision = Integer.parseInt(s); |
|
429 |
} |
|
430 |
else if (currentQname.equals("documentType")) { |
|
431 |
documentType = s; |
|
432 |
} |
|
433 |
else if (currentQname.equals("documentURL")) { |
|
434 |
documentURL = s; |
|
435 |
} |
|
436 |
|
|
437 |
currentQname = ""; |
|
440 |
public void error(SAXParseException e) throws SAXParseException { |
|
441 |
System.out.println("SAXParseException: " + e.getMessage()); |
|
442 |
throw e; |
|
443 |
} |
|
444 |
|
|
445 |
|
|
446 |
/** |
|
447 |
* Run the validating parser |
|
448 |
* |
|
449 |
* @param xml the xml stream to be validated |
|
450 |
* @schemaLocation relative path the to XML Schema file, e.g. "." |
|
451 |
* @exception IOException thrown when test files can't be opened |
|
452 |
* @exception ClassNotFoundException thrown when SAX Parser class not found |
|
453 |
* @exception SAXException |
|
454 |
* @exception SAXParserException |
|
455 |
*/ |
|
456 |
public void runParser(Reader xml, String schemaLocation) |
|
457 |
throws IOException, ClassNotFoundException, |
|
458 |
SAXException, SAXParseException { |
|
459 |
|
|
460 |
// Get an instance of the parser |
|
461 |
XMLReader parser; |
|
462 |
|
|
463 |
parser = XMLReaderFactory.createXMLReader(DEFAULT_PARSER); |
|
464 |
// Set Handlers in the parser |
|
465 |
parser.setContentHandler((ContentHandler)this); |
|
466 |
parser.setErrorHandler((ErrorHandler)this); |
|
467 |
parser.setFeature("http://xml.org/sax/features/namespaces", true); |
|
468 |
parser.setFeature("http://xml.org/sax/features/namespace-prefixes", true); |
|
469 |
parser.setFeature("http://xml.org/sax/features/validation", true); |
|
470 |
parser.setProperty( |
|
471 |
"http://apache.org/xml/properties/schema/external-schemaLocation", |
|
472 |
schemaLocation); |
|
473 |
|
|
474 |
if (schemaValidate) { |
|
475 |
parser.setFeature("http://apache.org/xml/features/validation/schema", |
|
476 |
true); |
|
438 | 477 |
} |
478 |
|
|
479 |
// Parse the document |
|
480 |
parser.parse(new InputSource(xml)); |
|
439 | 481 |
} |
482 |
/** |
|
483 |
* Handles a start-of-document event. |
|
484 |
*/ |
|
485 |
public void startDocument () { |
|
486 |
System.out.println("Started parsing " + documentListURL); |
|
487 |
} |
|
440 | 488 |
|
489 |
|
|
490 |
/** |
|
491 |
* Handles a start-of-element event. |
|
492 |
* |
|
493 |
* @param uri |
|
494 |
* @param localname |
|
495 |
* @param qname |
|
496 |
* @param attributes |
|
497 |
*/ |
|
498 |
public void startElement(String uri, |
|
499 |
String localname, |
|
500 |
String qname, |
|
501 |
Attributes attributes) { |
|
502 |
|
|
503 |
currentQname = qname; |
|
504 |
} |
|
441 | 505 |
} |
442 | 506 |
} |
Also available in: Unified diff
Additional development of Harvester implementation