Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: barteau $'
7
 *     '$Date: 2007-06-22 14:53:54 -0700 (Fri, 22 Jun 2007) $'
8
 * '$Revision: 3305 $'
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 */
24

    
25
package edu.ucsb.nceas.metacat.client;
26

    
27
import com.oreilly.servlet.multipart.FilePart;
28
import com.oreilly.servlet.multipart.MultipartParser;
29
import com.oreilly.servlet.multipart.ParamPart;
30
import com.oreilly.servlet.multipart.Part;
31
import edu.ucsb.nceas.metacat.MetaCatUtil;
32
import java.io.BufferedInputStream;
33
import java.io.BufferedReader;
34
import java.io.InputStream;
35
import java.io.InputStreamReader;
36
import java.io.PushbackReader;
37
import java.io.StringReader;
38
import java.io.IOException;
39
import java.io.StringWriter;
40
import java.io.Reader;
41
import java.net.URL;
42
import java.util.HashMap;
43
import java.util.Iterator;
44
import java.util.Map;
45
import java.util.Properties;
46
import java.util.TreeMap;
47
import java.util.Vector;
48
import javax.servlet.ServletContext;
49
import javax.servlet.http.HttpServletRequest;
50
import javax.servlet.http.HttpServletResponse;
51
import javax.xml.xpath.XPath;
52
import javax.xml.xpath.XPathFactory;
53
import org.w3c.dom.Document;
54
import org.w3c.dom.DocumentType;
55
import org.w3c.dom.Element;
56

    
57
import org.w3c.dom.Node;
58
import org.w3c.dom.NodeList;
59

    
60
import edu.ucsb.nceas.utilities.HttpMessage;
61
import edu.ucsb.nceas.utilities.IOUtil;
62
import edu.ucsb.nceas.utilities.XMLUtilities;
63
import java.io.File;
64
import javax.xml.xpath.XPathConstants;
65
import javax.xml.xpath.XPathExpressionException;
66

    
67

    
68
/**
69
 *  This interface provides methods for initializing and logging in to a
70
 *  Metacat server, and then querying, reading, transforming, inserting,
71
 *  updating and deleting documents from that server.
72
 */
73
public class MetacatClient implements Metacat {
74
    /** The URL string for the metacat server */
75
    private String metacatUrl;
76
    
77
    /** The session identifier for the session */
78
    private String sessionId;
79
    
80
    /**
81
     * Identifies the FGDC DTD.
82
     */
83
    public static final String              FGDC_SYSTEM_ID = "http://www.fgdc.gov/metadata/fgdc-std-001-1998.dtd";
84
    
85
    /**
86
     * Data Document ID location within an FGDC document.  XPath expression.
87
     */
88
    public static final String              FGDC_DATA_FILE_DOCID_XPATH = "/metadata/distinfo/stdorder/digform/digtopt/onlinopt/computer/networka/networkr";
89
    
90
    /**
91
     * Metadata Document ID location within an FGDC document.  XPath expression.
92
     */
93
    public static final String              FGDC_DOCID_XPATH = "/metadata/distinfo/resdesc";
94
    
95
    private static XPath                    xpath = XPathFactory.newInstance().newXPath();
96
    private Document                        loginResponse = null, metadataDoc = null;
97
    private String                          user = null;
98
    
99
    /**
100
     * The Login cookie name.
101
     */
102
    public final static String                    LOGIN_COOOKIE = "cookie";
103
    
104
    /**
105
     * Constructor to create a new instance. Protected because instances
106
     * should only be created by the factory MetacatFactory.
107
     */
108
    protected MetacatClient() {
109
        this.metacatUrl = null;
110
        this.sessionId = null;
111
    }
112
    
113
    /**
114
     *  Method used to log in to a metacat server. Implementations will need
115
     *  to cache a cookie value to make the session persistent.  Each time a
116
     *  call is made to one of the other methods (e.g., read), the cookie will
117
     *  need to be passed back to the metacat server along with the request.
118
     *
119
     *  @param username   the username of the user, like an LDAP DN
120
     *  @param password   the password for that user for authentication
121
     *  @return the response string from metacat in XML format
122
     *  @throws MetacatAuthException when the username/password could
123
     *                    not be authenticated
124
     */
125
    public String login(String username, String password)
126
    throws MetacatAuthException, MetacatInaccessibleException {
127
        Properties prop = new Properties();
128
        prop.put("action", "login");
129
        prop.put("qformat", "xml");
130
        prop.put("username", username);
131
        prop.put("password", password);
132
        
133
        String response = null;
134
        try {
135
            response = sendDataForString(prop, null, null, 0);
136
        } catch (Exception e) {
137
            throw new MetacatInaccessibleException(e.getMessage());
138
        }
139
        
140
        if (response.indexOf("<login>") == -1) {
141
            setSessionId("");
142
            throw new MetacatAuthException(response);
143
        } else {
144
            int start = response.indexOf("<sessionId>") + 11;
145
            int end = response.indexOf("</sessionId>");
146
            if ((start != -1) && (end != -1)) {
147
                setSessionId(response.substring(start,end));
148
            }
149
        }
150
        return response;
151
    }
152
    
153
    /**
154
     *  Method used to log in to a metacat server. Implementations will need
155
     *  to cache a cookie value to make the session persistent.  Each time a
156
     *  call is made to one of the other methods (e.g., read), the cookie will
157
     *  need to be passed back to the metacat server along with the request.
158
     *
159
     *  @param username   the username of the user, like an LDAP DN
160
     *  @param password   the password for that user for authentication
161
     *  @return the response string from metacat in XML format
162
     *  @throws MetacatAuthException when the username/password could
163
     *                    not be authenticated
164
     */
165
    public String getloggedinuserinfo() throws MetacatInaccessibleException {
166
        Properties prop = new Properties();
167
        prop.put("action", "getloggedinuserinfo");
168
        prop.put("qformat", "xml");
169
        
170
        String response = null;
171
        try {
172
            response = sendDataForString(prop, null, null, 0);
173
        } catch (Exception e) {
174
            throw new MetacatInaccessibleException(e.getMessage());
175
        }
176
        
177
        return response;
178
    }
179
    
180
    /**
181
     *  Method used to log out a metacat server. The Metacat server will end
182
     *  the session when this call is invoked.
183
     *
184
     *  @return the response string from metacat in XML format
185
     *  @throws MetacatInaccessibleException when the metacat server can not be
186
     *                                    reached or does not respond
187
     */
188
    public String logout() throws MetacatInaccessibleException, MetacatException {
189
        Properties prop = new Properties();
190
        prop.put("action", "logout");
191
        prop.put("qformat", "xml");
192
        
193
        String response = null;
194
        try {
195
            response = sendDataForString(prop, null, null, 0);
196
        } catch (Exception e) {
197
            throw new MetacatInaccessibleException(e.getMessage());
198
        }
199
        
200
        if (response.indexOf("<logout>") == -1) {
201
            throw new MetacatException(response);
202
        }
203
        setSessionId("");
204
        return response;
205
    }
206
    
207
    /**
208
     * Read an XML document from the metacat server session, accessed by docid,
209
     * and returned as a Reader.
210
     *
211
     * @param docid the identifier of the document to be read
212
     * @return a Reader for accessing the document
213
     * @throws InsufficientKarmaException when the user has insufficent rights
214
     *                                    for the operation
215
     * @throws MetacatInaccessibleException when the metacat server can not be
216
     *                                    reached or does not respond
217
     * @throws MetacatException when the metacat server generates another error
218
     */
219
    public Reader read(String docid) throws InsufficientKarmaException,
220
            MetacatInaccessibleException, MetacatException, DocumentNotFoundException {
221
        PushbackReader pbr = null;
222
        
223
        Properties prop = new Properties();
224
        prop.put("action", "read");
225
        prop.put("qformat", "xml");
226
        prop.put("docid", docid);
227
        InputStream response = null;
228
        try {
229
            response = sendData(prop, null, null, 0);
230
        } catch (Exception e) {
231
            throw new MetacatInaccessibleException(e.getMessage());
232
        }
233
        pbr = new PushbackReader(new InputStreamReader(response), 512);
234
        try {
235
            char[] characters = new char[512];
236
            int len = pbr.read(characters, 0, 512);
237
            StringWriter sw = new StringWriter();
238
            sw.write(characters, 0, len);
239
            String message = sw.toString();
240
            sw.close();
241
            pbr.unread(characters, 0, len);
242
            if (message.indexOf("<error>") != -1) {
243
                if (message.indexOf("does not have permission") != -1) {
244
                    throw new InsufficientKarmaException(message);
245
                } else if(message.indexOf("does not exist") != -1) {
246
                    throw new DocumentNotFoundException(message);
247
                } else {
248
                    throw new MetacatException(message);
249
                }
250
            }
251
        } catch (IOException ioe) {
252
            throw new MetacatException(
253
                    "MetacatClient: Error converting Reader to String."
254
                    + ioe.getMessage());
255
        }
256
        return pbr;
257
    }
258
    
259
    
260
    /**
261
     * Read inline data from the metacat server session, accessed by
262
     * inlinedataid and returned as a Reader.
263
     *
264
     * @param inlinedataid the identifier of the data to be read
265
     * @return a Reader for accessing the document
266
     * @throws InsufficientKarmaException when the user has insufficent rights
267
     *                                    for the operation
268
     * @throws MetacatInaccessibleException when the metacat server can not be
269
     *                                    reached or does not respond
270
     * @throws MetacatException when the metacat server generates another error
271
     */
272
    public Reader readInlineData(String inlinedataid)
273
    throws InsufficientKarmaException,
274
            MetacatInaccessibleException, MetacatException {
275
        PushbackReader pbr = null;
276
        
277
        Properties prop = new Properties();
278
        prop.put("action", "readinlinedata");
279
        prop.put("inlinedataid", inlinedataid);
280
        
281
        InputStream response = null;
282
        try {
283
            response = sendData(prop, null, null, 0);
284
        } catch (Exception e) {
285
            throw new MetacatInaccessibleException(e.getMessage());
286
        }
287
        
288
        pbr = new PushbackReader(new InputStreamReader(response), 512);
289
        try {
290
            char[] characters = new char[512];
291
            int len = pbr.read(characters, 0, 512);
292
            StringWriter sw = new StringWriter();
293
            sw.write(characters, 0, len);
294
            String message = sw.toString();
295
            sw.close();
296
            pbr.unread(characters, 0, len);
297
            
298
            if (message.indexOf("<error>") != -1) {
299
                if (message.indexOf("does not have permission") != -1) {
300
                    throw new InsufficientKarmaException(message);
301
                } else {
302
                    throw new MetacatException(message);
303
                }
304
            }
305
        } catch (IOException ioe) {
306
            throw new MetacatException(
307
                    "MetacatClient: Error converting Reader to String."
308
                    + ioe.getMessage());
309
        }
310
        
311
        return pbr;
312
    }
313
    
314
    /**
315
     * Query the metacat document store with the given metacat-compatible
316
     * query document, and return the result set as a Reader.
317
     *
318
     * @param xmlQuery a Reader for accessing the XML version of the query
319
     * @return a Reader for accessing the result set
320
     */
321
    public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
322
            IOException {
323
        Reader reader = null;
324
        String query = null;
325
        try {
326
            query = IOUtil.getAsString(xmlQuery, true);
327
        } catch (IOException ioE) {
328
            throw ioE;
329
        }
330
        
331
        //set up properties
332
        Properties prop = new Properties();
333
        prop.put("action", "squery");
334
        prop.put("qformat", "xml");
335
        prop.put("query", query);
336
        
337
        InputStream response = null;
338
        try {
339
            response = sendData(prop, null, null, 0);
340
        } catch (Exception e) {
341
            throw new MetacatInaccessibleException(e.getMessage());
342
        }
343
        reader = new InputStreamReader(response);
344
        return reader;
345
    }
346
    
347
    /**
348
     * Insert an XML document into the repository.
349
     *
350
     * @param docid the docid to insert the document
351
     * @param xmlDocument a Reader for accessing the XML document to be inserted
352
     * @param schema a Reader for accessing the DTD or XML Schema for
353
     *               the document
354
     * @return the metacat response message
355
     * @throws InsufficientKarmaException when the user has insufficent rights
356
     *                                    for the operation
357
     * @throws MetacatInaccessibleException when the metacat server can not be
358
     *                                    reached or does not respond
359
     * @throws MetacatException when the metacat server generates another error
360
     * @throws IOException when there is an error reading the xml document
361
     */
362
    public String insert(String docid, Reader xmlDocument, Reader schema)
363
    throws InsufficientKarmaException, MetacatException, IOException,
364
            MetacatInaccessibleException {
365
        Reader reader = null;
366
        String doctext = null;
367
        String schematext = null;
368
        try {
369
            doctext = IOUtil.getAsString(xmlDocument, true);
370
            if (schema != null) {
371
                schematext = IOUtil.getAsString(schema, true);
372
            }
373
        } catch (IOException ioE) {
374
            throw ioE;
375
        }
376
        
377
        //set up properties
378
        Properties prop = new Properties();
379
        prop.put("action", "insert");
380
        prop.put("docid", docid);
381
        prop.put("doctext", doctext);
382
        if (schematext != null) {
383
            prop.put("dtdtext", schematext);
384
        }
385
        
386
        String response = null;
387
        try {
388
            response = sendDataForString(prop, null, null, 0);
389
        } catch (Exception e) {
390
            throw new MetacatInaccessibleException(e.getMessage());
391
        }
392
        
393
        // Check for an error condition
394
        if (response.indexOf("<error>") != -1) {
395
            if (response.indexOf("does not have permission") != -1) {
396
                throw new InsufficientKarmaException(response);
397
            } else {
398
                throw new MetacatException(response);
399
            }
400
        }
401
        
402
        return response;
403
    }
404
    
405
    /**
406
     * Update an XML document in the repository.
407
     *
408
     * @param docid the docid to update
409
     * @param xmlDocument a Reader for accessing the XML text to be updated
410
     * @param schema a Reader for accessing the DTD or XML Schema for
411
     *               the document
412
     * @return the metacat response message
413
     * @throws InsufficientKarmaException when the user has insufficent rights
414
     *                                    for the operation
415
     * @throws MetacatInaccessibleException when the metacat server can not be
416
     *                                    reached or does not respond
417
     * @throws MetacatException when the metacat server generates another error
418
     * @throws IOException when there is an error reading the xml document
419
     */
420
    public String update(String docid, Reader xmlDocument, Reader schema)
421
    throws InsufficientKarmaException, MetacatException, IOException,
422
            MetacatInaccessibleException {
423
        Reader reader = null;
424
        String doctext = null;
425
        String schematext = null;
426
        try {
427
            doctext = IOUtil.getAsString(xmlDocument, true);
428
            if (schema != null) {
429
                schematext = IOUtil.getAsString(schema, true);
430
            }
431
        } catch (IOException ioE) {
432
            throw ioE;
433
        }
434
        
435
        //set up properties
436
        Properties prop = new Properties();
437
        prop.put("action", "update");
438
        prop.put("docid", docid);
439
        prop.put("doctext", doctext);
440
        if (schematext != null) {
441
            prop.put("dtdtext", schematext);
442
        }
443
        
444
        String response = null;
445
        try {
446
            response = sendDataForString(prop, null, null, 0);
447
        } catch (Exception e) {
448
            throw new MetacatInaccessibleException(e.getMessage());
449
        }
450
        
451
        // Check for an error condition
452
        if (response.indexOf("<error>") != -1) {
453
            if (response.indexOf("does not have permission") != -1) {
454
                throw new InsufficientKarmaException(response);
455
            } else {
456
                throw new MetacatException(response);
457
            }
458
        }
459
        
460
        return response;
461
    }
462
    
463
    /**
464
     * Upload a data document into the repository.
465
     *
466
     * @param docid the docid to insert the document
467
     * @param document a Reader for accessing the document to be uploaded
468
     * @return the metacat response message
469
     * @throws InsufficientKarmaException when the user has insufficent rights
470
     *                                    for the operation
471
     * @throws MetacatInaccessibleException when the metacat server can not be
472
     *                                    reached or does not respond
473
     * @throws MetacatException when the metacat server generates another error
474
     * @throws IOException when there is an error reading the xml document
475
     */
476
    public String upload(String docid, File file)
477
    throws InsufficientKarmaException, MetacatException, IOException,
478
            MetacatInaccessibleException {
479
        
480
        URL url = new URL(metacatUrl.trim());
481
        HttpMessage msg = new HttpMessage(url);
482
        //set up properties
483
        Properties arg = new Properties();
484
        arg.put("action", "upload");
485
        arg.put("docid", docid);
486
        
487
        Properties filenames = new Properties();
488
        String filename = file.getAbsolutePath();
489
        filenames.put("datafile", filename);
490
        
491
        String response = null;
492
        try {
493
            response = sendDataForString(arg, filenames, null, 0);
494
        } catch (Exception e) {
495
            throw new MetacatInaccessibleException(e.getMessage());
496
        }
497
        
498
        // Check for an error condition
499
        if (response.indexOf("<error>") != -1) {
500
            if (response.indexOf("does not have permission") != -1) {
501
                throw new InsufficientKarmaException(response);
502
            } else {
503
                throw new MetacatException(response);
504
            }
505
        }
506
        
507
        return response;
508
    }
509
    
510
    /**
511
     * Upload a data document into the repository.
512
     *
513
     * @param docid the docid to insert the document
514
     * @param document a Reader for accessing the document to be uploaded
515
     * @return the metacat response message
516
     * @throws InsufficientKarmaException when the user has insufficent rights
517
     *                                    for the operation
518
     * @throws MetacatInaccessibleException when the metacat server can not be
519
     *                                    reached or does not respond
520
     * @throws MetacatException when the metacat server generates another error
521
     * @throws IOException when there is an error reading the xml document
522
     */
523
    
524
    
525
    public String upload(String docid, String filename, InputStream fileData,
526
            int size)
527
            throws InsufficientKarmaException, MetacatException, IOException,
528
            MetacatInaccessibleException {
529
        
530
        URL url = new URL(metacatUrl.trim());
531
        HttpMessage msg = new HttpMessage(url);
532
        //set up properties
533
        Properties arg = new Properties();
534
        arg.put("action", "upload");
535
        arg.put("docid", docid);
536
        
537
        Properties filenames = new Properties();
538
        filenames.put("datafile", filename);
539
        
540
        String response = null;
541
        try {
542
            response = sendDataForString(arg, filenames, fileData, size);
543
        } catch (Exception e) {
544
            throw new MetacatInaccessibleException(e.getMessage());
545
        }
546
        
547
        // Check for an error condition
548
        if (response.indexOf("<error>") != -1) {
549
            if (response.indexOf("does not have permission") != -1) {
550
                throw new InsufficientKarmaException(response);
551
            } else {
552
                throw new MetacatException(response);
553
            }
554
        }
555
        
556
        return response;
557
    }
558
    
559
    /**
560
     * Delete an XML document in the repository.
561
     *
562
     * @param docid the docid to delete
563
     * @return the metacat response message
564
     * @throws InsufficientKarmaException when the user has insufficent rights
565
     *                                    for the operation
566
     * @throws MetacatInaccessibleException when the metacat server can not be
567
     *                                    reached or does not respond
568
     * @throws MetacatException when the metacat server generates another error
569
     */
570
    public String delete(String docid)
571
    throws InsufficientKarmaException, MetacatException,
572
            MetacatInaccessibleException {
573
        //set up properties
574
        Properties prop = new Properties();
575
        prop.put("action", "delete");
576
        prop.put("docid", docid);
577
        
578
        String response = null;
579
        try {
580
            response = sendDataForString(prop, null, null, 0);
581
        } catch (Exception e) {
582
            throw new MetacatInaccessibleException(e.getMessage());
583
        }
584
        
585
        // Check for an error condition
586
        if (response.indexOf("<error>") != -1) {
587
            if (response.indexOf("does not have permission") != -1) {
588
                throw new InsufficientKarmaException(response);
589
            } else {
590
                throw new MetacatException(response);
591
            }
592
        }
593
        return response;
594
    }
595
    
596
    
597
    /**
598
     * set the access on an XML document in the repository.
599
     *
600
     * @param _docid the docid of the document for which the access should be applied.
601
     *
602
     * @param _principal the document's principal
603
     *
604
     * @param _permission the access permission to be applied to the docid
605
     *  {e.g. read,write,all}
606
     *
607
     * @param _permType the permission type to be applied to the document
608
     *  {e.g. allow or deny}
609
     *
610
     * @param _permOrder the order that the document's permissions should be
611
     *  processed {e.g. denyFirst or allowFirst}
612
     *
613
     *
614
     * @return the metacat response message
615
     *
616
     * @throws InsufficientKarmaException when the user has insufficent rights
617
     *                                    for the operation
618
     * @throws MetacatInaccessibleException when the metacat server can not be
619
     *                                    reached or does not respond
620
     * @throws MetacatException when the metacat server generates another error
621
     */
622
    public String setAccess(String _docid, String _principal, String
623
            _permission, String _permType,
624
            String _permOrder )
625
            throws InsufficientKarmaException, MetacatException,
626
            MetacatInaccessibleException {
627
        //set up properties
628
        Properties prop = new Properties();
629
        prop.put("action", "setaccess");
630
        prop.put("docid", _docid);
631
        prop.put("principal", _principal);
632
        prop.put("permission", _permission);
633
        prop.put("permType", _permType);
634
        prop.put("permOrder", _permOrder);
635
        
636
        String response = null;
637
        try {
638
            response = sendDataForString(prop, null, null, 0);
639
        } catch (Exception e) {
640
            throw new MetacatInaccessibleException(e.getMessage());
641
        }
642
        
643
        // Check for an error condition
644
        if (response.indexOf("<error>") != -1) {
645
            if (response.indexOf("does not have permission") != -1) {
646
                throw new InsufficientKarmaException(response);
647
            } else {
648
                throw new MetacatException(response);
649
            }
650
        }
651
        return response;
652
    }
653
    
654
    /**
655
     * When the MetacatFactory creates an instance it needs to set the
656
     * MetacatUrl to which connections should be made.
657
     *
658
     * @param metacatUrl the URL for the metacat server
659
     */
660
    public void setMetacatUrl(String metacatUrl) {
661
        this.metacatUrl = metacatUrl;
662
    }
663
    
664
    /**
665
     * Get the session identifier for this session.  This is only valid if
666
     * the login methods has been called successfully for this Metacat object
667
     * beforehand.
668
     *
669
     * @returns the sessionId as a String, or null if the session is invalid
670
     */
671
    public String getSessionId() {
672
        return this.sessionId;
673
    }
674
    
675
    /**
676
     * Set the session identifier for this session.  This identifier was
677
     * previously established with a call to login.  To continue to use the
678
     * same session, set the session id before making a call to one of the
679
     * metacat access methods (e.g., read, query, insert, etc.).
680
     *
681
     * @param String the sessionId from a previously established session
682
     */
683
    public void setSessionId(String sessionId) {
684
        this.sessionId = sessionId;
685
    }
686
    
687
    /**
688
     * The method will return the latest revision in metacat server
689
     * for a given document id. If some error happens, this method will throw
690
     * an exception.
691
     * @param docId String  the given docid you want to use. the docid it self
692
     *                      can have or haven't revision number
693
     * @throws MetacatException
694
     */
695
    public int getNewestDocRevision(String docId) throws MetacatException {
696
        int rev = 0;
697
        //set up properties
698
        Properties prop = new Properties();
699
        prop.put("action", "getrevisionanddoctype");
700
        prop.put("docid", docId);
701
        
702
        String response = null;
703
        try {
704
            response = sendDataForString(prop, null, null, 0);
705
            //parseRevisionResponse will return null if there is an
706
            //error that it can't handle
707
            String revStr = parserRevisionResponse(response);
708
            Integer revObj = new Integer(revStr);
709
            rev = revObj.intValue();
710
            // Check for an error condition
711
            if (response.indexOf("<error>") != -1 && revStr == null) {
712
                throw new MetacatException(response);
713
            }
714
        } catch (Exception e) {
715
            throw new MetacatException(e.getMessage());
716
        }
717
        return rev;
718
    }
719
    
720
    /**
721
     * Return the highest document id for a given scope.  This is used by
722
     * clients to make it easier to determine the next free identifier in a
723
     * sequence for a given scope.
724
     * @param scope String  the scope to use for looking up the latest id
725
     * @throws MetacatException when an error occurs
726
     */
727
    public String getLastDocid(String scope) throws MetacatException {
728
        String lastIdentifier = "";
729
        //set up properties
730
        Properties prop = new Properties();
731
        prop.put("action", "getlastdocid");
732
        prop.put("scope", scope);
733
        
734
        String response = null;
735
        try {
736
            response = sendDataForString(prop, null, null, 0);
737
            // Check for an error condition
738
            if (response.indexOf("<error>") != -1) {
739
                throw new MetacatException(response);
740
            } else {
741
                Reader responseReader = new StringReader(response);
742
                Node root =
743
                        XMLUtilities.getXMLReaderAsDOMTreeRootNode(responseReader);
744
                Node docidNode =
745
                        XMLUtilities.getNodeWithXPath(root, "/lastDocid/docid");
746
                lastIdentifier = docidNode.getFirstChild().getNodeValue();
747
            }
748
        } catch (Exception e) {
749
            throw new MetacatException(e.getMessage());
750
        }
751
        return lastIdentifier;
752
    }
753
    
754
    /**
755
     * return a list of all docids that match a given scope.  if scope is null
756
     * return all docids registered in the system
757
     * @param scope String  the scope to use to limit the docid query
758
     * @throws MetacatException when an error occurs
759
     */
760
    public Vector getAllDocids(String scope) throws MetacatException {
761
        Vector resultVec = new Vector();
762
        //set up properties
763
        Properties prop = new Properties();
764
        prop.put("action", "getalldocids");
765
        if(scope != null) {
766
            prop.put("scope", scope);
767
        }
768
        
769
        String response = null;
770
        try {
771
            response = sendDataForString(prop, null, null, 0);
772
            // Check for an error condition
773
            if (response.indexOf("<error>") != -1) {
774
                throw new MetacatException(response);
775
            } else {
776
                Reader responseReader = new StringReader(response);
777
                Node root =
778
                        XMLUtilities.getXMLReaderAsDOMTreeRootNode(responseReader);
779
                NodeList nlist = root.getChildNodes();
780
                for(int i=0; i<nlist.getLength(); i++) {
781
                    Node n = nlist.item(i);
782
                    if(n.getNodeName().equals("docid")) {
783
                        //add the content to the return vector
784
                        String nodeVal = n.getFirstChild().getNodeValue();
785
                        resultVec.addElement(nodeVal);
786
                    }
787
                }
788
                
789
            }
790
        } catch (Exception e) {
791
            throw new MetacatException(e.getMessage());
792
        }
793
        return resultVec;
794
    }
795
    
796
    /**
797
     * return true of the given docid is registered, false if not
798
     * @param scope String  the scope to use to limit the docid query
799
     * @throws MetacatException when an error occurs
800
     */
801
    public boolean isRegistered(String docid) throws MetacatException {
802
        Vector resultVec = new Vector();
803
        //set up properties
804
        Properties prop = new Properties();
805
        prop.put("action", "isregistered");
806
        if(docid == null) {
807
            throw new MetacatException("<error>Cannot check if a null docid " +
808
                    "is registered.</error>");
809
        }
810
        prop.put("docid", docid);
811
        
812
        String response = null;
813
        try {
814
            response = sendDataForString(prop, null, null, 0);
815
            // Check for an error condition
816
            if (response.indexOf("<error>") != -1) {
817
                throw new MetacatException(response);
818
            } else {
819
                Reader responseReader = new StringReader(response);
820
                StringBuffer sb = new StringBuffer();
821
                char[] c = new char[1024];
822
                int numread = responseReader.read(c, 0, 1024);
823
                while(numread != -1) {
824
                    sb.append(new String(c, 0, numread));
825
                    numread = responseReader.read(c, 0, 1024);
826
                }
827
                
828
                String responseStr = sb.toString();
829
                if(responseStr.indexOf("true") != -1) {
830
                    return true;
831
                }
832
                return false;
833
            }
834
        } catch (Exception e) {
835
            throw new MetacatException(e.getMessage());
836
        }
837
    }
838
    
839
    /************************************************************************
840
     * PRIVATE METHODS
841
     ************************************************************************/
842
    
843
    /**
844
     * Send a request to metacat.
845
     *
846
     * @param prop the properties to be URL encoded and sent
847
     * @param filename  the properties to be sent to Metacat
848
     *                  in case of upload, otherwise null
849
     * @param fileData  the inputStream for the file data to be sent to Metacat
850
     *                  in case of upload, otherwise null
851
     * @param size      the size of the data being sent to Metacat
852
     *                  in case of upload, otherwise 0
853
     */
854
    synchronized private InputStream sendDataOnce(Properties args,
855
            Properties filename,
856
            InputStream fileData,
857
            int size)
858
            throws Exception {
859
        InputStream returnStream = null;
860
        URL url = new URL(metacatUrl);
861
        HttpMessage msg = new HttpMessage(url);
862
        msg.setCookie("JSESSIONID="+this.sessionId);
863
        if (filename == null){
864
            returnStream = msg.sendPostData(args);
865
        } else if (fileData == null){
866
            returnStream = msg.sendPostData(args, filename);
867
        } else if (size > 0) {
868
            returnStream = msg.sendPostData(args, filename, fileData, size);
869
        } else {
870
            throw new MetacatException("Invalid size specified for " +
871
                    "the input stream being passed");
872
        }
873
        return returnStream;
874
    }
875
    
876
    /**
877
     * Send a request to Metacat
878
     *
879
     * @param args  the properties to be sent to Metacat
880
     * @param filename  the properties to be sent to Metacat
881
     *                  in case of upload, otherwise null
882
     * @param fileData  the inputStream for the file data to be sent to Metacat
883
     *                  in case of upload, otherwise null
884
     * @param size      the size of the data being sent to Metacat
885
     *                  in case of upload, otherwise 0
886
     * @return      InputStream as returned by Metacat
887
     */
888
    synchronized private InputStream sendData(Properties args,
889
            Properties filename,
890
            InputStream fileData,
891
            int size)
892
            throws Exception {
893
        InputStream returnStream = null;
894
        /*
895
            Note:  The reason that there are three try statements all executing
896
            the same code is that there is a problem with the initial connection
897
            using the HTTPClient protocol handler.  These try statements make
898
            sure that a connection is made because it gives each connection a
899
            2nd and 3rd chance to work before throwing an error.
900
            THIS IS A TOTAL HACK.  THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
901
            RELEASE OF MORPHO!!!  cwb (7/24/01)
902
         */
903
        try {
904
            return sendDataOnce(args, filename, fileData, size);
905
        } catch (Exception e) {
906
            try {
907
                return sendDataOnce(args, filename, fileData, size);
908
            } catch (Exception e2) {
909
                try {
910
                    return sendDataOnce(args, filename, fileData, size);
911
                } catch (Exception e3) {
912
                    System.err.println(
913
                            "Failed to send data to metacat 3 times: " + e3.getMessage());
914
                    System.err.println("metacaturl: " + metacatUrl);
915
                    throw e3;
916
                }
917
            }
918
        }
919
    }
920
    
921
    /**
922
     * Send a request to Metacat
923
     *
924
     * @param args      the properties to be sent to Metacat
925
     * @param filename  the properties to be sent to Metacat
926
     *                  in case of upload, otherwise null
927
     * @param fileData  the inputStream for the file data to be sent to Metacat
928
     *                  in case of upload, otherwise null
929
     * @param size      the size of the data being sent to Metacat
930
     *                  in case of upload, otherwise 0
931
     * @return          a string as returned by Metacat
932
     */
933
    synchronized private String sendDataForString(Properties args,
934
            Properties filename,
935
            InputStream fileData,
936
            int size)
937
            throws Exception {
938
        String response = null;
939
        
940
        try {
941
            InputStreamReader returnStream =
942
                    new InputStreamReader(sendData(args, filename,
943
                    fileData, size));
944
            StringWriter sw = new StringWriter();
945
            int len;
946
            char[] characters = new char[512];
947
            while ((len = returnStream.read(characters, 0, 512)) != -1) {
948
                sw.write(characters, 0, len);
949
            }
950
            returnStream.close();
951
            response = sw.toString();
952
            sw.close();
953
        } catch (Exception e) {
954
            throw e;
955
        }
956
        return response;
957
    }
958
    
959
    /*
960
     * "getversionanddoctype" action will return a string from metacat server.
961
     * The string format is "revision;doctype"(This is bad idea, we should use xml)
962
     * This method will get revision string from the response string
963
     */
964
    private String parserRevisionResponse(String response) throws Exception {
965
        String revision = null;
966
        if (response != null) {
967
            if(response.indexOf("<error>") != -1) {
968
                if(response.indexOf("There is not record") != -1) {
969
                    return "0";
970
                } else {
971
                    return null;
972
                }
973
            } else {
974
                int firstSemiCol = response.indexOf(";");
975
                revision = response.substring(0, firstSemiCol);
976
            }
977
        }
978
        return revision;
979
    }
980
    
981
    /**
982
     * JSP API: This is a convenience method to reduce the amount of code in a Metacat Client
983
     * JSP.  It handles creating/reusing an instance of a MetacatClient.
984
     * @param request Since this is intended to be used by a JSP, it is passed the
985
     * available "request" variable (the HttpServletRequest).
986
     * @throws edu.ucsb.nceas.metacat.client.MetacatInaccessibleException Received by MetacatFactory.
987
     * @return MetacatClient instance.
988
     */
989
    public static MetacatClient getMetacatClient(javax.servlet.http.HttpServletRequest request) throws MetacatInaccessibleException {
990
        MetacatClient                       result;
991
        String                              metacatPath = "http://%1$s%2$s/metacat";
992
        String                              host, context;
993
        javax.servlet.http.HttpSession      session;
994
        
995
        session = request.getSession();
996
        result = (MetacatClient) session.getAttribute("MetacatClient");
997
        if (result == null) {
998
            host = request.getHeader("host");
999
            context = request.getContextPath();
1000
            metacatPath = String.format(metacatPath, host, context);
1001
            result = (MetacatClient) MetacatFactory.createMetacatConnection(metacatPath);
1002
            session.setAttribute("MetacatClient", result);
1003
        }
1004
        return(result);
1005
    }
1006
    
1007
    /**
1008
     * JSP API: When the user logs in, the server will send back a string containing XML.  Calling
1009
     * this method with the string will allow other methods to work, such as isLoggIn()
1010
     * and getLoginResponseElement().
1011
     * @param xmlString XML in String format.
1012
     * @throws java.io.IOException Input/Output exception.
1013
     */
1014
    public void setLoginResponse(String xmlString) throws IOException {
1015
        if (xmlString != null) {
1016
            loginResponse = XMLUtilities.getXMLReaderAsDOMDocument(new StringReader(xmlString));
1017
        }
1018
    }
1019
    
1020
    private void setMetadataDoc(InputStream ioStream) throws IOException {
1021
        BufferedReader                          buffy;
1022
        
1023
        if (ioStream != null) {
1024
            buffy = new BufferedReader(new InputStreamReader(ioStream));
1025
            metadataDoc = XMLUtilities.getXMLReaderAsDOMDocument(buffy);
1026
        }
1027
    }
1028
    
1029
    public void setMetadataDoc(Document doc) {
1030
        metadataDoc = doc;
1031
    }
1032
    
1033
    public void setMetadataDoc(String docId) throws Exception {
1034
        Document                        doc = null;
1035
        BufferedReader                  buffy;
1036
        Properties                      prop;
1037
        InputStream                     response;
1038
        
1039
        //*** MetaCatServlet Properties: action, qformat and docid. ***
1040
        prop = new Properties();
1041
        prop.put("action", "read");
1042
        prop.put("qformat", "xml");
1043
        prop.put("docid", docId);
1044
        response = sendData(prop, null, null, 0);
1045
        if (response != null) {
1046
            buffy = new BufferedReader(new InputStreamReader(response));
1047
            doc = XMLUtilities.getXMLReaderAsDOMDocument(buffy);
1048
        }
1049
        setMetadataDoc(doc);
1050
    }
1051
    
1052
    public Document getMetadataDoc() {
1053
        return(metadataDoc);
1054
    }
1055
    
1056
    /**
1057
     * JSP API: A convenient and efficient method to retrieve info from the "login response".
1058
     * @param elementName String of the elementName.
1059
     * NOTE: setLoginResponse() must have been called first,
1060
     * otherwise it will always return null.
1061
     * @throws javax.xml.xpath.XPathExpressionException XPath error.
1062
     * @return String containing the text content of the element, or null.
1063
     */
1064
    public String getLoginResponseElement(String elementName) throws XPathExpressionException {
1065
        String                      result = null;
1066
        
1067
        if (loginResponse != null) {
1068
            result = (String) xpath.evaluate(elementName, loginResponse.getDocumentElement(), XPathConstants.STRING);
1069
            if (result != null)
1070
                result = result.trim();
1071
        }
1072
        return(result);
1073
    }
1074
    
1075
    
1076
    /**
1077
     * JSP API: Easy way to get info from an uploaded metadata XML file.
1078
     * Note:  doMetadataUpload() must have been called first.
1079
     * @param elementName String containing the elemement name
1080
     * @throws javax.xml.xpath.XPathExpressionException Thrown by XPath
1081
     * @return String text content of the named element
1082
     */
1083
    public String getMetadataDocElement(String elementName) throws XPathExpressionException {
1084
        String                      result = null;
1085
        
1086
        if (metadataDoc != null) {
1087
            result = (String) xpath.evaluate(elementName, metadataDoc.getDocumentElement(), XPathConstants.STRING);
1088
            result = result.trim();
1089
        }
1090
        return(result);
1091
    }
1092
    
1093
    /**
1094
     * JSP API: A convenience method to be used by JSP or any other client code that requires
1095
     * the user to be logged in.  NOTE: setUser() must have been called first,
1096
     * otherwise it will always return false.
1097
     * @return boolean  true if user has logged in for this session, false otherwise.
1098
     */
1099
    public boolean isLoggedIn() {
1100
        return(user != null);
1101
    }
1102
    
1103
    /**
1104
     * JSP API: Get the user's name.  This will be what was entered for the user name,
1105
     * and is not the LDAP user name.
1106
     * @return String of the users name.
1107
     */
1108
    public String getUser() {
1109
        return(user);
1110
    }
1111
    
1112
    /**
1113
     * JSP API: After calling "login(ldapUserName, pwd)", call this with the username
1114
     * and servers response message.  You can than use isLoggedIn() to determine if
1115
     * the user is logged in, getLoginResponseElement(), etc.  The user name will also
1116
     * used by calls to doMetadataUpload() for Document Id creation (scope).
1117
     * @param userName User name
1118
     * @param serverResponse XML login response sent from Metacat.
1119
     */
1120
    public void setUser(String userName, String serverResponse) {
1121
        
1122
        if (serverResponse != null && serverResponse.contains("login")) {
1123
            user = userName;
1124
            System.out.println("MetacatClient.setUser: getSessionId() = " + getSessionId());
1125
        } else {
1126
            user = null;
1127
        }
1128
    }
1129
    
1130
    /**
1131
     * JSP API:  Handles metadata file and data file uploads for inserting new
1132
     * Metacat data packages.  Note: if content type is not "multipart/form-data",
1133
     * nothing will happen; thus, it's safe to be (unintentionally) called by other
1134
     * types of form submissions.
1135
     * @param request HTTP request.
1136
     * @return A 1-line status message for the user.
1137
     */
1138
    public String doMetadataUpload(javax.servlet.http.HttpServletRequest request) {
1139
        String                      result = "", contentType, formatType;
1140
        String                      lastDocId, nextDocId, metaDocId;
1141
        StringBuilder               fileName = new StringBuilder();
1142
        Reader                      reader;
1143
        int                         sizeLimit, idx;
1144
        MultipartParser             multipartParser;
1145
        InputStream                 inputStream;
1146
        Node                        newBranch, metaRootNode;
1147
        HashMap                     dataDocIDs, paramsMap;
1148
        
1149
        
1150
        ServletContext cntxt = request.getSession().getServletContext().getContext("/knb/metacat");
1151
        String servletName = cntxt.getServletContextName();
1152
        
1153
        //*** Only process request if a file upload.
1154
        contentType = request.getContentType();
1155
        if (isLoggedIn() && contentType != null && contentType.contains("multipart/form-data")) {
1156
            try {
1157
                //*** Init the MultipartParser.
1158
                sizeLimit = (new Integer(MetaCatUtil.getOption("datafilesizelimit"))).intValue();
1159
                multipartParser = new MultipartParser(request, sizeLimit * 1024 * 1024);
1160
                
1161
                //*** Get the First file, which should be the metadata file.
1162
                paramsMap = new HashMap();
1163
                inputStream = getNextInputStream(multipartParser, fileName, paramsMap);
1164
                if (fileName.toString().toLowerCase().endsWith(".xml")) {
1165
                    setMetadataDoc(inputStream);
1166
                    
1167
                    //*** Get the Metadata File's DOC ID.
1168
                    lastDocId = getLastDocid(user);
1169
                    metaDocId = lastDocId = nextDocId(lastDocId);
1170
                    
1171
                    if (isFGDC()) {
1172
                        //*** Loop thru all of the data files, get fileName and inputStream.
1173
                        dataDocIDs = new HashMap();
1174
                        fileName = new StringBuilder();
1175
                        while ((inputStream = getNextInputStream(multipartParser, fileName, paramsMap)) != null) {
1176
                            //*** Get the data file's DOC ID.
1177
                            nextDocId = nextDocId(lastDocId);
1178
                            //*** Set the file format (just using file extension for now).
1179
                            idx = fileName.lastIndexOf(".");
1180
                            if (idx > 1)
1181
                                formatType = fileName.substring(idx+1).toUpperCase();
1182
                            else
1183
                                formatType = "";
1184
                            dataDocIDs.put(nextDocId, formatType);
1185
                            //*** Upload the data file to metacat.
1186
                            upload(nextDocId, fileName.toString(), inputStream, Integer.MAX_VALUE);
1187
                            
1188
                            lastDocId = nextDocId;
1189
                            fileName = new StringBuilder();
1190
                        }
1191
                        
1192
                        //*** Store the User Name and Doc Id in the FGDC document.
1193
                        newBranch = getFGDCdisinfo(getLoginResponseElement("name"), metaDocId, dataDocIDs);
1194
                        System.out.println("MetacatClient.doMetadataUpload: " + XMLUtilities.getDOMTreeAsString(newBranch));
1195
                        metaRootNode = addDistInfoToFGDC(newBranch);
1196
                        
1197
                        //*** Upload the metadata file to metacat.
1198
                        reader = XMLUtilities.getDOMTreeAsReader(metadataDoc.getDocumentElement(), false);
1199
                        insert(metaDocId, reader, null);
1200
                        
1201
                        result = "MetaCat Package Inserted:  the Document Identifier is " + metaDocId;
1202
                        reader.close();
1203
                        
1204
                        //*** One last detail...grant the public read access.
1205
                        if (paramsMap.containsKey("publicAccess"))
1206
                            setAccess(metaDocId, "public", "read", "allow", "allowFirst");
1207
                        
1208
                    } else {
1209
                        System.out.println("MetacatClient.doUpload: not an FGDC file = " + fileName);
1210
                        result = fileName + " is not an FGDC file.  Files not uploaded.";
1211
                        //TODO add other types of metadata grammars here...
1212
                    }
1213
                } else {
1214
                    result = "The first file must be an XML Metadata file.  Files not uploaded.";
1215
                }
1216
                if (inputStream != null)
1217
                    inputStream.close();
1218
            } catch (MetacatException ex)  {
1219
                result = ex.getMessage();
1220
                System.out.println("MetacatClient.doUpload: MetacatException = " + result);
1221
            } catch (IOException ex)  {
1222
                System.out.println("MetacatClient.doUpload: " + ex);
1223
            } catch (Exception ex) {
1224
                System.out.println("MetacatClient.doUpload: " + ex);
1225
            } catch (Error err) {
1226
                System.out.println("MetacatClient.doUpload: ERR - " + err.getCause());
1227
                result = "ERR: " + err.getMessage();
1228
            }
1229
        }
1230
        return(result);
1231
    }
1232
    
1233
    private InputStream getNextInputStream(MultipartParser multipartParser, StringBuilder fileName, HashMap paramsMap)
1234
    throws IOException {
1235
        InputStream                     result = null;
1236
        Part                            part;
1237
        String                          parmName = null, value = null, fnam;
1238
        
1239
        while ((part = multipartParser.readNextPart()) != null) {
1240
            if (part.isParam()) {
1241
                parmName = part.getName();
1242
                value = ((ParamPart) part).getStringValue();
1243
                paramsMap.put(parmName, value);
1244
                System.out.println("MetacatClient.doUpload: parmName = " + parmName + "  value = " + value);
1245
                
1246
            } else if (part.isFile()) {
1247
                fnam = ((FilePart) part).getFileName();
1248
                if (fnam != null && !fnam.equals("")) {
1249
                    //*** File name is passed back via StringBuilder fileName param.
1250
                    fileName.append(fnam);
1251
                    result = ((FilePart) part).getInputStream();
1252
                    System.out.println("MetacatClient.doUpload: fileName = " + fileName + "  inputStream = " + result.toString());
1253
                    break;
1254
                }
1255
            }
1256
        }
1257
        return(result);
1258
    }
1259
    
1260
    private String nextDocId(String lastDocId) {
1261
        String                      result = null, tokens[];
1262
        int                         vers;
1263
        String                      template = user.toLowerCase() + ".%1$d.%2$d";
1264
        
1265
        if(lastDocId != null && lastDocId.contains(".")) {
1266
            lastDocId = lastDocId.replace('.','~'); //*** This is necessary for the split to work.
1267
            tokens = lastDocId.split("~");
1268
            if(tokens.length > 1 && !tokens[1].equals("")) {
1269
                try {
1270
                    vers = Integer.parseInt(tokens[1]);
1271
                    result = String.format(template, 1 + vers, 1);
1272
                } catch (NumberFormatException ex) {
1273
                    //*** In case the lastDocId has something other than a number.
1274
                    result = String.format(template, 1, 1);
1275
                }
1276
            } else {
1277
                //*** In case the lastDocId ends with a '.'
1278
                result = String.format(template, 1, 1);
1279
            }
1280
        } else {
1281
            //*** In case there isn't any doc Id's with the user name.
1282
            result = String.format(template, 1, 1);
1283
        }
1284
        return(result);
1285
    }
1286
    
1287
    private String nextVersion(String lastDocId) throws XPathExpressionException {
1288
        String                      result = null, tokens[], scope, xPathQuery, ready2Split;
1289
        int                         vers, docNum;
1290
        final int                   LAST_TOKEN = 2;
1291
        final String                TEMPLATE = "%1$s.%2$d.%3$d";
1292
        final String                XPATH_QUERY_TEMPLATE = FGDC_DOCID_XPATH + "[text()='%1$s']";
1293
        Node                        node;
1294
        
1295
        //*** Parse the last Doc Id, and increment the version number.
1296
        if(lastDocId != null && lastDocId.contains(".")) {
1297
            ready2Split = lastDocId.replace('.','~'); //*** This is necessary for the split to work.
1298
            tokens = ready2Split.split("~");
1299
            if(tokens.length > LAST_TOKEN && !tokens[LAST_TOKEN].equals("")) {
1300
                scope = tokens[LAST_TOKEN - 2];
1301
                docNum = Integer.parseInt(tokens[LAST_TOKEN - 1]);
1302
                try {
1303
                    vers = Integer.parseInt(tokens[LAST_TOKEN]);
1304
                    result = String.format(TEMPLATE, scope, docNum, 1 + vers);
1305
                } catch (NumberFormatException ex) {
1306
                    //*** In case the lastDocId has something other than a number.
1307
                    result = String.format(TEMPLATE, scope, docNum, 1);
1308
                }
1309
            } else {
1310
                //*** In case the lastDocId ends with a '.'
1311
                result = lastDocId + "1";
1312
            }
1313
        } else {
1314
            //*** In case of missing doc Id.
1315
            result = null;
1316
        }
1317
        
1318
        //*** Update the Doc Id in the metadata file.
1319
        if (getMetadataDoc() != null) {
1320
            xPathQuery = String.format(XPATH_QUERY_TEMPLATE, lastDocId);
1321
            node = (Node) xpath.evaluate(xPathQuery, getMetadataDoc().getDocumentElement(), XPathConstants.NODE);
1322
            node.setTextContent(result);
1323
        }
1324
        return(result);
1325
    }
1326
    
1327
    private boolean isFGDC() {
1328
        boolean                     result = false;
1329
        DocumentType                docType;
1330
        String                      sysId;
1331
        final String                FGDC_TEST_EXPRESSION = "/metadata/idinfo/citation/citeinfo/title";
1332
        Node                        node = null;
1333
        
1334
        //*** First, try the rigid proper way of determining it.
1335
        if (getMetadataDoc() != null) {
1336
            docType = getMetadataDoc().getDoctype();
1337
            if (docType != null) {
1338
                sysId = docType.getSystemId();
1339
                if (sysId != null)
1340
                    result = sysId.contains(FGDC_SYSTEM_ID);
1341
            }
1342
        }
1343
        //*** It might not have a doc type line, so try another method.
1344
        if (getMetadataDoc() != null && !result) {
1345
            try {
1346
                node = (Node) xpath.evaluate(FGDC_TEST_EXPRESSION, getMetadataDoc().getDocumentElement(), XPathConstants.NODE);
1347
            } catch (XPathExpressionException ex) {
1348
                ex.printStackTrace();
1349
            }
1350
            result = (node != null);
1351
        }
1352
        return(result);
1353
    }
1354
    
1355
    private Node getFGDCdisinfo(String contactName, String resourceDescription, HashMap dataDocIDs) throws IOException {
1356
        Node                        result = null, node, digformBranch, formname, stdorder;
1357
        Document                    doc;
1358
        Iterator                    iterIt;
1359
        String                      key, value;
1360
        
1361
        //*** This is a valid/minimal FGDC "distinfo" branch.
1362
        final String XML = "<distinfo>"
1363
                + "    <distrib>"
1364
                + "        <cntinfo>"
1365
                + "            <cntperp>"
1366
                + "                <cntper></cntper>"
1367
                + "            </cntperp>"
1368
                + "            <cntaddr>"
1369
                + "                <addrtype></addrtype>"
1370
                + "                <address></address>"
1371
                + "                <city></city>"
1372
                + "                <state></state>"
1373
                + "                <postal></postal>"
1374
                + "                <country></country>"
1375
                + "            </cntaddr>"
1376
                + "            <cntvoice></cntvoice>"
1377
                + "        </cntinfo>"
1378
                + "    </distrib>"
1379
                + "    <resdesc></resdesc>"
1380
                + "    <distliab></distliab>"
1381
                + "    <stdorder>"
1382
                + "        <digform>"
1383
                + "            <digtinfo>"
1384
                + "                <formname></formname>"
1385
                + "            </digtinfo>"
1386
                + "            <digtopt>"
1387
                + "                <onlinopt>"
1388
                + "                    <computer>"
1389
                + "                        <networka>"
1390
                + "                            <networkr></networkr>"
1391
                + "                        </networka>"
1392
                + "                    </computer>"
1393
                + "                </onlinopt>"
1394
                + "            </digtopt>"
1395
                + "        </digform>"
1396
                + "        <fees></fees>"
1397
                + "    </stdorder>"
1398
                + "</distinfo>";
1399
        
1400
        doc = XMLUtilities.getXMLReaderAsDOMDocument(new StringReader(XML));
1401
        result = doc.getDocumentElement();
1402
        try {
1403
            //*** Set the Contact Person.
1404
            node = (Node) xpath.evaluate("/distinfo/distrib/cntinfo/cntperp/cntper", result, XPathConstants.NODE);
1405
            node.setTextContent(contactName);
1406
            //*** Set the metadata Doc Id.
1407
            node = (Node) xpath.evaluate("/distinfo/resdesc", result, XPathConstants.NODE);
1408
            node.setTextContent(resourceDescription);
1409
            
1410
            //*** Loop thru the files, setting their format and Doc Id.
1411
            stdorder = (Node) xpath.evaluate("/distinfo/stdorder", result, XPathConstants.NODE);
1412
            digformBranch = (Node) xpath.evaluate("/distinfo/stdorder/digform", result, XPathConstants.NODE);
1413
            iterIt = dataDocIDs.keySet().iterator();
1414
            while(iterIt.hasNext()) {
1415
                //*** Save the data file Doc ID (required).
1416
                key = (String) iterIt.next();
1417
                node = (Node) xpath.evaluate("digtopt/onlinopt/computer/networka/networkr", digformBranch, XPathConstants.NODE);
1418
                node.setTextContent(key);
1419
                //*** Save the data file format (optional).
1420
                formname = (Node) xpath.evaluate("digtinfo/formname", digformBranch, XPathConstants.NODE);
1421
                if ((value = (String) dataDocIDs.get(key)) != null && !value.equals("")) {
1422
                    formname.setTextContent(value);
1423
                } else {
1424
                    //*** We did a deep clone of the branch, so clear prior contents.
1425
                    formname.setTextContent("");
1426
                }
1427
                
1428
                //*** Clone branch for next file.
1429
                if (iterIt.hasNext()) {
1430
                    digformBranch = digformBranch.cloneNode(true);
1431
                    stdorder.appendChild(digformBranch);
1432
                }
1433
            }
1434
        } catch (XPathExpressionException ex) {
1435
            ex.printStackTrace();
1436
        }
1437
        return(result);
1438
    }
1439
    
1440
    private Node addDistInfoToFGDC(Node newBranch) {
1441
        Node                        result = null, node;
1442
        
1443
        if (newBranch != null) {
1444
            result = metadataDoc.getDocumentElement();
1445
            try {
1446
                //*** Get a reference to the FGDC required "metainfo" node (only 1 allowed).
1447
                node = (Node) xpath.evaluate("/metadata/metainfo", result, XPathConstants.NODE);
1448
                if (node != null) {
1449
                    newBranch = metadataDoc.importNode(newBranch, true);
1450
                    //*** Add the new "distinfo" before it.
1451
                    result.insertBefore(newBranch, node);
1452
                }
1453
            } catch (XPathExpressionException ex) {
1454
                ex.printStackTrace();
1455
            }
1456
        }
1457
        return(result);
1458
    }
1459
    
1460
    private static final String                SELECT_TEMPLATE = "<select name='%1$s' style='%2$s' size='%3$s'>\n%4$s</select>\n";
1461
    private static final String                OPTION_TEMPLATE = "<option value='%1$s'>%2$s</option>\n";
1462
    private static final String                OPTGRP_TEMPLATE = "<optgroup label='%1$s'>%2$s</optgroup>";
1463
    private static final String                INPUT_TEMPLATE = "<input name='%1$s' value='%2$s' type='%4$s' class='%3$s' size='%5$d'/>\n";
1464
    
1465
    /**
1466
     * JSP API: A static helper method which takes a vector (keys/values) and returns
1467
     * an XHTML SELECT String.
1468
     * @param vector The vector contianing the values to convert into an HTML SELECT statement.
1469
     * @param name The name to assign the HTML SELECT, which will become the parameter name.
1470
     * @param style Any HTML styling text.
1471
     * @param size HTML field width.
1472
     * @return String, XHTML for a SELECT statement.
1473
     */
1474
    public static String vectorToHtmlSelect(Vector vector, String name, String style, int size) {
1475
        String                      result = "", item;
1476
        Iterator<String>            iterIt;
1477
        
1478
        iterIt = vector.iterator();
1479
        while(iterIt.hasNext()) {
1480
            item = iterIt.next();
1481
            item = String.format(OPTION_TEMPLATE, item, item);
1482
            result += item;
1483
        }
1484
        result = String.format(SELECT_TEMPLATE, name, style, size, result);
1485
        return(result);
1486
    }
1487
    
1488
    /**
1489
     * JSP API: A static helper method which takes a map (key, value pairs) and returns
1490
     * an XHTML SELECT String.
1491
     * @param map The map contianing the key, value pairs to convert into an HTML SELECT
1492
     * statement.
1493
     * @param name The name to assign the HTML SELECT, which will become the parameter name.
1494
     * @param style Any HTML styling text.
1495
     * @param size HTML field width.
1496
     * @return String, XHTML for a SELECT statement.
1497
     */
1498
    public static String mapToHtmlSelect(Map map, String name, String style, int size) {
1499
        String                      result = "", item, key, optGrp;
1500
        Iterator<String>            iterIt;
1501
        Object                      obj;
1502
        Vector                      vector;
1503
        Iterator                    completeIterIt;
1504
        
1505
        iterIt = map.keySet().iterator();
1506
        while(iterIt.hasNext()) {
1507
            key = iterIt.next();
1508
            obj = map.get(key);
1509
            if (obj instanceof String) {
1510
                item = (String) obj;
1511
                item = String.format(OPTION_TEMPLATE, key, item);
1512
                result += item;
1513
            } else if (obj instanceof Vector) {
1514
                vector = (Vector) obj;
1515
                optGrp = "";
1516
                completeIterIt = vector.iterator();
1517
                while (completeIterIt.hasNext()) {
1518
                    item = (String) completeIterIt.next();
1519
                    item = String.format(OPTION_TEMPLATE, item, item);
1520
                    optGrp += item;
1521
                }
1522
                item = String.format(OPTGRP_TEMPLATE, key, optGrp);
1523
                result += item;
1524
            }
1525
        }
1526
        result = String.format(SELECT_TEMPLATE, name, style, size, result);
1527
        return(result);
1528
    }
1529
    
1530
    
1531
    /**
1532
     * JSP API: A static helper method which takes attribute values and returns
1533
     * an XHTML INPUT Statement.
1534
     * @param type The 'type attribute' of the input statement.
1535
     * @param name The 'name attribute' of the input statement.
1536
     * @param value The 'value attribute' of the input statement.
1537
     * @param cssClass The CSS 'class attribute'.
1538
     * @param fldSize HTML field width.
1539
     * @return String, XHTML for an INPUT statement.
1540
     */
1541
    public static String htmlInput(String type, String name, String value, String cssClass, int fldSize) {
1542
        String                          result;
1543
        
1544
        result = String.format(INPUT_TEMPLATE, name, value, cssClass, type, fldSize);
1545
        return(result);
1546
    }
1547
    
1548
    
1549
    /**
1550
     * JSP API: A static helper method which takes a key/value pair and saves
1551
     * it in the session.
1552
     * @param key The session attribute key name to use.
1553
     * @param val The value to assign.
1554
     * @param request The HttpServletRequest.
1555
     */
1556
    public static void setSessValue(String key, String val, HttpServletRequest request) {
1557
        javax.servlet.http.HttpSession      session;
1558
        
1559
        session = request.getSession();
1560
        if (key != null && !key.equals("")) {
1561
            session.setAttribute(key, val);
1562
        }
1563
    }
1564
    
1565
    
1566
    /**
1567
     * JSP API: A static helper method which takes a key and returns the
1568
     * value.
1569
     * @param key The session attribute key name to use.
1570
     * @param request The HttpServletRequest.
1571
     * @return String value.
1572
     */
1573
    public static String getSessValue(String key, HttpServletRequest request) {
1574
        javax.servlet.http.HttpSession      session;
1575
        String                              result = "";
1576
        
1577
        if (key != null && !key.equals("") && request != null) {
1578
            session = request.getSession();
1579
            result = (String) session.getAttribute(key);
1580
        }
1581
        result = (result == null ? "" : result);
1582
        return(result);
1583
    }
1584
    
1585
    /**
1586
     * Query metacat for documents that 'CONTAINS' the value at the specified XPath
1587
     * expression.  Additionally, returns another non-standard field value.
1588
     * Standard info contains: DocId, DocName, DocType, CreateDate, and UpdateDate.
1589
     * @param pathExpr String contianing an XPath expression.
1590
     * @param pathValue String containing a comparison value at the XPath expression.
1591
     * @param returnFld String containing an XPath expression to a field which will be returned
1592
     * in addition to the standard info.
1593
     * @return DOM Document containing the results.
1594
     */
1595
    public Document query(String pathExpr, String pathValue, String returnFld) {
1596
        Document                        result = null;
1597
        InputStream                     response;
1598
        BufferedReader                  buffy;
1599
        Properties                      prop;
1600
        
1601
        try {
1602
            prop = new Properties();
1603
            prop.put("action", "query");
1604
            prop.put("qformat", "xml");
1605
            prop.put(pathExpr, pathValue);
1606
            if (returnFld != null) {
1607
                prop.put("returnfield", returnFld);
1608
            }
1609
            
1610
            response = sendData(prop, null, null, 0);
1611
            if (response != null) {
1612
                buffy = new BufferedReader(new InputStreamReader(response));
1613
                result = XMLUtilities.getXMLReaderAsDOMDocument(buffy);
1614
            }
1615
        } catch (IOException ex) {
1616
            ex.printStackTrace();
1617
        } catch (Exception ex) {
1618
            ex.printStackTrace();
1619
        }
1620
        return(result);
1621
    }
1622
    
1623
    /**
1624
     * Queries Metacat for document listings, and returns the results in a TreeMap,
1625
     * where the key is the Doc Id, and the value is the Create Date.  If the document
1626
     * contains the specified 'returnfield', an addtional entry will be created with
1627
     * the value being a Vector of sub-DocId's.  The key of this entry will be the
1628
     * original DocId with some addtional text added.
1629
     * Reads request parameters 'pathExpr' (String[]), 'pathValue' (String)
1630
     * and 'returnfield' (String).
1631
     * @param request HttpServletRequest.
1632
     * @return TreeMap
1633
     */
1634
    public TreeMap getSelectQueryMap(HttpServletRequest request) {
1635
        TreeMap                         result;
1636
        Document                        doc;
1637
        NodeList                        nodeLst, subNodeLst;
1638
        Node                            node, subNode;
1639
        String                          key, val, paramExpr, paramVal;
1640
        String                          value, paths[], returnFld;
1641
        Vector                          optGroup;
1642
        final String                    DOCID_EXPR = "./docid";
1643
        final String                    DOCNAME_EXPR = "./createdate";
1644
        final String                    PARAM_EXPR = "./param[@name='%1$s']";
1645
        
1646
        paths = (String[]) request.getParameterValues("pathExpr");
1647
        returnFld = (String) request.getParameter("returnfield");
1648
        value = (String) request.getParameter("pathValue");
1649
        
1650
        result = new TreeMap();
1651
        paramExpr = String.format(PARAM_EXPR, returnFld);
1652
        
1653
        for(int j = 0; j < paths.length; j++) {
1654
            //*** Query the database ***
1655
            doc = query(paths[j], value, returnFld);
1656
            //*** Build the TreeMap to return ***
1657
            try {
1658
                nodeLst = (NodeList) xpath.evaluate("/resultset/document", doc, XPathConstants.NODESET);
1659
                for (int i = 0; i < nodeLst.getLength(); i++) {
1660
                    node = nodeLst.item(i);
1661
                    key = xpath.evaluate(DOCID_EXPR, node);
1662
                    val = xpath.evaluate(DOCNAME_EXPR, node);
1663
                    result.put(key, key + " (" + val + ")");
1664
                    
1665
                    //*** returnfield values ***
1666
                    subNodeLst = (NodeList) xpath.evaluate(paramExpr, node, XPathConstants.NODESET);
1667
                    if (subNodeLst.getLength() > 0) {
1668
                        optGroup = new Vector();
1669
                        for (int k = 0; k < subNodeLst.getLength(); k++) {
1670
                            subNode =  subNodeLst.item(k);
1671
                            paramVal = xpath.evaluate("text()", subNode);
1672
                            optGroup.add(paramVal);
1673
                        }
1674
                        result.put(key + " Data Files", optGroup);
1675
                    }
1676
                    
1677
                }
1678
            } catch (XPathExpressionException ex) {
1679
                ex.printStackTrace();
1680
            }
1681
        }
1682
        return(result);
1683
    }
1684
    
1685
    private String removeDataDocIdFromFGDC(String docId, String parentDocId) throws Exception {
1686
        String                          pathToDigform, revision = "";
1687
        Document                        doc;
1688
        InputStream                     response;
1689
        BufferedReader                  buffy;
1690
        Properties                      prop;
1691
        Node                            node;
1692
        NodeList                        nodeLst;
1693
        Reader                          reader;
1694
        final String                    PATH4ANCESTOR = FGDC_DATA_FILE_DOCID_XPATH + "[text()='%1$s']/ancestor::node()[name()='%2$s']";
1695
        
1696
        //*** Get the metadata document and remove the digform branch.
1697
        doc = getMetadataDoc();
1698
        if (doc != null) {
1699
            //System.out.println("MetacatClinet.removeDataDocIdFromFGDC: BEFORE\n" + XMLUtilities.getDOMTreeAsString(doc.getDocumentElement()));
1700
            pathToDigform = String.format(PATH4ANCESTOR, docId, "digform");
1701
            node = (Node) xpath.evaluate(pathToDigform, doc.getDocumentElement(), XPathConstants.NODE);
1702
            node.getParentNode().removeChild(node);
1703
            //System.out.println("MetacatClinet.removeDataDocIdFromFGDC: AFTER\n" + XMLUtilities.getDOMTreeAsString(doc.getDocumentElement()));
1704
            
1705
            revision = nextVersion(parentDocId);
1706
            reader = XMLUtilities.getDOMTreeAsReader(doc.getDocumentElement(), false);
1707
            update(revision, reader, null);
1708
        }
1709
        return(revision);
1710
    }
1711
    
1712
    /**
1713
     * Removes the data Doc Id from the FGDC document in the Metacat database.
1714
     * Determines what
1715
     * metadata file is including this file.  It then queries metacat for the parent FGDC
1716
     * document and removes the Doc Id from it, and reloads the new version of the FGDC
1717
     * document with a new revision number.  This is intended to be called after
1718
     * a call to MetacatClient.delete(docId).
1719
     * @param docId String which contains the document Id.
1720
     * @return String server response, if any.
1721
     */
1722
    public void clientDeleteRequest(HttpServletRequest request) {
1723
        String                      result = null, docId, subDocId, parentDocId, revisedDocId;
1724
        NodeList                    nodeLst;
1725
        final String                SUB_DOCS_PATH = FGDC_DATA_FILE_DOCID_XPATH + "/text()";
1726
        Node                        node;
1727
        Document                    resultSetDoc;
1728
        
1729
        docId = getSessValue("docId", request);
1730
        try {
1731
            //*** First, determine what metadata file is including this file (if any).
1732
            resultSetDoc = query(FGDC_DATA_FILE_DOCID_XPATH, docId, null);
1733
            parentDocId = xpath.evaluate("/resultset/document/docid", resultSetDoc.getDocumentElement());
1734
            if (parentDocId != null && !parentDocId.equals("")) {
1735
                setMetadataDoc(parentDocId);
1736
                //*** Remove Doc Id from any parent metadata document.
1737
                revisedDocId = removeDataDocIdFromFGDC(docId, parentDocId);
1738
                setSessValue("docId", revisedDocId, request);
1739
            } else {
1740
                setMetadataDoc(docId);
1741
                //*** This is a metadata document, so remove all of the sub-docId's.
1742
                nodeLst = (NodeList) xpath.evaluate(SUB_DOCS_PATH, getMetadataDoc().getDocumentElement(), XPathConstants.NODESET);
1743
                for(int i = 0; i < nodeLst.getLength(); i++) {
1744
                    node = nodeLst.item(i);
1745
                    subDocId = node.getNodeValue();
1746
                    //*** Remove the sub-document.
1747
                    try {
1748
                        delete(subDocId);
1749
                    } catch (MetacatException ex) {
1750
                        ex.printStackTrace();
1751
                    }
1752
                }
1753
            }
1754
            //*** Remove the document.
1755
            result = delete(docId);
1756
            //*** Save the server feedback in the session, to be used by the view.
1757
            setSessValue("updateFeedback", result, request);
1758
        } catch (Exception ex) {
1759
            ex.printStackTrace();
1760
        }
1761
    }
1762
}
(5-5/8)