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: sgarg $'
7
 *     '$Date: 2004-09-02 14:38:51 -0700 (Thu, 02 Sep 2004) $'
8
 * '$Revision: 2264 $'
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 java.io.BufferedReader;
28
import java.io.InputStream;
29
import java.io.InputStreamReader;
30
import java.io.PushbackReader;
31
import java.io.IOException;
32
import java.io.StringWriter;
33
import java.io.Reader;
34
import java.net.URL;
35
import java.util.Properties;
36

    
37
import edu.ucsb.nceas.utilities.HttpMessage;
38
import edu.ucsb.nceas.utilities.IOUtil;
39
import java.io.File;
40

    
41

    
42
/**
43
 *  This interface provides methods for initializing and logging in to a
44
 *  Metacat server, and then querying, reading, transforming, inserting,
45
 *  updating and deleting documents from that server.
46
 */
47
public class MetacatClient implements Metacat
48
{
49
    /** The URL string for the metacat server */
50
    private String metacatUrl;
51

    
52
    /** The session identifier for the session */
53
    private String sessionId;
54

    
55
    /**
56
     * Constructor to create a new instance. Protected because instances
57
     * should only be created by the factory MetacatFactory.
58
     */
59
    protected MetacatClient()
60
    {
61
        this.metacatUrl = null;
62
        this.sessionId = null;
63
    }
64

    
65
    /**
66
     *  Method used to log in to a metacat server. Implementations will need
67
     *  to cache a cookie value to make the session persistent.  Each time a
68
     *  call is made to one of the other methods (e.g., read), the cookie will
69
     *  need to be passed back to the metacat server along with the request.
70
     *
71
     *  @param username   the username of the user, like an LDAP DN
72
     *  @param password   the password for that user for authentication
73
     *  @return the response string from metacat in XML format
74
     *  @throws MetacatAuthException when the username/password could
75
     *                    not be authenticated
76
     */
77
    public String login(String username, String password)
78
           throws MetacatAuthException, MetacatInaccessibleException
79
    {
80
        Properties prop = new Properties();
81
        prop.put("action", "login");
82
        prop.put("qformat", "xml");
83
        prop.put("username", username);
84
        prop.put("password", password);
85

    
86
        String response = null;
87
        try {
88
            response = sendDataForString(prop, null, null, 0);
89
        } catch (Exception e) {
90
            throw new MetacatInaccessibleException(e.getMessage());
91
        }
92

    
93
        if (response.indexOf("<login>") == -1) {
94
            setSessionId("");
95
            throw new MetacatAuthException(response);
96
        } else {
97
            int start = response.indexOf("<sessionId>") + 11;
98
            int end = response.indexOf("</sessionId>");
99
            if ((start != -1) && (end != -1)) {
100
                setSessionId(response.substring(start,end));
101
            }
102
        }
103
        return response;
104
    }
105

    
106
    /**
107
     *  Method used to log out a metacat server. The Metacat server will end
108
     *  the session when this call is invoked.
109
     *
110
     *  @return the response string from metacat in XML format
111
     *  @throws MetacatInaccessibleException when the metacat server can not be
112
     *                                    reached or does not respond
113
     */
114
    public String logout() throws MetacatInaccessibleException, MetacatException
115
    {
116
        Properties prop = new Properties();
117
        prop.put("action", "logout");
118
        prop.put("qformat", "xml");
119

    
120
        String response = null;
121
        try {
122
            response = sendDataForString(prop, null, null, 0);
123
        } catch (Exception e) {
124
            throw new MetacatInaccessibleException(e.getMessage());
125
        }
126

    
127
        if (response.indexOf("<logout>") == -1) {
128
            throw new MetacatException(response);
129
        }
130
        setSessionId("");
131
        return response;
132
    }
133

    
134
    /**
135
     * Read an XML document from the metacat server session, accessed by docid,
136
     * and returned as a Reader.
137
     *
138
     * @param docid the identifier of the document to be read
139
     * @return a Reader for accessing the document
140
     * @throws InsufficientKarmaException when the user has insufficent rights
141
     *                                    for the operation
142
     * @throws MetacatInaccessibleException when the metacat server can not be
143
     *                                    reached or does not respond
144
     * @throws MetacatException when the metacat server generates another error
145
     */
146
    public Reader read(String docid) throws InsufficientKarmaException,
147
        MetacatInaccessibleException, MetacatException
148
    {
149
        PushbackReader pbr = null;
150

    
151
        Properties prop = new Properties();
152
        prop.put("action", "read");
153
        prop.put("qformat", "xml");
154
        prop.put("docid", docid);
155

    
156
        InputStream response = null;
157
        try {
158
            response = sendData(prop, null, null, 0);
159
        } catch (Exception e) {
160
            throw new MetacatInaccessibleException(e.getMessage());
161
        }
162

    
163
        pbr = new PushbackReader(new InputStreamReader(response), 512);
164
        try {
165
            char[] characters = new char[512];
166
            int len = pbr.read(characters, 0, 512);
167
            StringWriter sw = new StringWriter();
168
            sw.write(characters, 0, len);
169
            String message = sw.toString();
170
            sw.close();
171
            pbr.unread(characters, 0, len);
172

    
173
            if (message.indexOf("<error>") != -1) {
174
                if (message.indexOf("does not have permission") != -1) {
175
                    throw new InsufficientKarmaException(message);
176
                } else {
177
                    throw new MetacatException(message);
178
                }
179
            }
180
        } catch (IOException ioe) {
181
            throw new MetacatException(
182
                    "MetacatClient: Error converting Reader to String."
183
                    + ioe.getMessage());
184
        }
185

    
186
        return pbr;
187
    }
188

    
189

    
190
    /**
191
        * Read inline data from the metacat server session, accessed by
192
        * inlinedataid and returned as a Reader.
193
        *
194
        * @param inlinedataid the identifier of the data to be read
195
        * @return a Reader for accessing the document
196
        * @throws InsufficientKarmaException when the user has insufficent rights
197
        *                                    for the operation
198
        * @throws MetacatInaccessibleException when the metacat server can not be
199
        *                                    reached or does not respond
200
        * @throws MetacatException when the metacat server generates another error
201
        */
202
       public Reader readInlineData(String inlinedataid)
203
           throws InsufficientKarmaException,
204
           MetacatInaccessibleException, MetacatException
205
       {
206
           PushbackReader pbr = null;
207

    
208
           Properties prop = new Properties();
209
           prop.put("action", "readinlinedata");
210
           prop.put("inlinedataid", inlinedataid);
211

    
212
           InputStream response = null;
213
           try {
214
               response = sendData(prop, null, null, 0);
215
           } catch (Exception e) {
216
               throw new MetacatInaccessibleException(e.getMessage());
217
           }
218

    
219
           pbr = new PushbackReader(new InputStreamReader(response), 512);
220
           try {
221
               char[] characters = new char[512];
222
               int len = pbr.read(characters, 0, 512);
223
               StringWriter sw = new StringWriter();
224
               sw.write(characters, 0, len);
225
               String message = sw.toString();
226
               sw.close();
227
               pbr.unread(characters, 0, len);
228

    
229
               if (message.indexOf("<error>") != -1) {
230
                   if (message.indexOf("does not have permission") != -1) {
231
                       throw new InsufficientKarmaException(message);
232
                   } else {
233
                       throw new MetacatException(message);
234
                   }
235
               }
236
           } catch (IOException ioe) {
237
               throw new MetacatException(
238
                       "MetacatClient: Error converting Reader to String."
239
                       + ioe.getMessage());
240
           }
241

    
242
           return pbr;
243
       }
244

    
245
    /**
246
     * Query the metacat document store with the given metacat-compatible
247
     * query document, and return the result set as a Reader.
248
     *
249
     * @param xmlQuery a Reader for accessing the XML version of the query
250
     * @return a Reader for accessing the result set
251
     */
252
    public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
253
                                                IOException
254
    {
255
        Reader reader = null;
256
        String query = null;
257
        try {
258
          query = IOUtil.getAsString(xmlQuery, true);
259
        } catch (IOException ioE) {
260
          throw ioE;
261
        }
262

    
263
        //set up properties
264
        Properties prop = new Properties();
265
        prop.put("action", "squery");
266
        prop.put("qformat", "xml");
267
        prop.put("query", query);
268

    
269
        InputStream response = null;
270
        try {
271
            response = sendData(prop, null, null, 0);
272
        } catch (Exception e) {
273
            throw new MetacatInaccessibleException(e.getMessage());
274
        }
275
        reader = new InputStreamReader(response);
276
        return reader;
277
    }
278

    
279
    /**
280
     * Insert an XML document into the repository.
281
     *
282
     * @param docid the docid to insert the document
283
     * @param xmlDocument a Reader for accessing the XML document to be inserted
284
     * @param schema a Reader for accessing the DTD or XML Schema for
285
     *               the document
286
     * @return the metacat response message
287
     * @throws InsufficientKarmaException when the user has insufficent rights
288
     *                                    for the operation
289
     * @throws MetacatInaccessibleException when the metacat server can not be
290
     *                                    reached or does not respond
291
     * @throws MetacatException when the metacat server generates another error
292
     * @throws IOException when there is an error reading the xml document
293
     */
294
    public String insert(String docid, Reader xmlDocument, Reader schema)
295
        throws InsufficientKarmaException, MetacatException, IOException,
296
        MetacatInaccessibleException
297
    {
298
        Reader reader = null;
299
        String doctext = null;
300
        String schematext = null;
301
        try {
302
          doctext = IOUtil.getAsString(xmlDocument, true);
303
          if (schema != null) {
304
              schematext = IOUtil.getAsString(schema, true);
305
          }
306
        } catch (IOException ioE) {
307
          throw ioE;
308
        }
309

    
310
        //set up properties
311
        Properties prop = new Properties();
312
        prop.put("action", "insert");
313
        prop.put("docid", docid);
314
        prop.put("doctext", doctext);
315
        if (schematext != null) {
316
            prop.put("dtdtext", schematext);
317
        }
318

    
319
        String response = null;
320
        try {
321
            response = sendDataForString(prop, null, null, 0);
322
        } catch (Exception e) {
323
            throw new MetacatInaccessibleException(e.getMessage());
324
        }
325

    
326
        // Check for an error condition
327
        if (response.indexOf("<error>") != -1) {
328
            if (response.indexOf("does not have permission") != -1) {
329
                throw new InsufficientKarmaException(response);
330
            } else {
331
                throw new MetacatException(response);
332
            }
333
        }
334

    
335
        return response;
336
    }
337

    
338
    /**
339
     * Update an XML document in the repository.
340
     *
341
     * @param docid the docid to update
342
     * @param xmlDocument a Reader for accessing the XML text to be updated
343
     * @param schema a Reader for accessing the DTD or XML Schema for
344
     *               the document
345
     * @return the metacat response message
346
     * @throws InsufficientKarmaException when the user has insufficent rights
347
     *                                    for the operation
348
     * @throws MetacatInaccessibleException when the metacat server can not be
349
     *                                    reached or does not respond
350
     * @throws MetacatException when the metacat server generates another error
351
     * @throws IOException when there is an error reading the xml document
352
     */
353
    public String update(String docid, Reader xmlDocument, Reader schema)
354
        throws InsufficientKarmaException, MetacatException, IOException,
355
        MetacatInaccessibleException
356
    {
357
        Reader reader = null;
358
        String doctext = null;
359
        String schematext = null;
360
        try {
361
          doctext = IOUtil.getAsString(xmlDocument, true);
362
          if (schema != null) {
363
              schematext = IOUtil.getAsString(schema, true);
364
          }
365
        } catch (IOException ioE) {
366
          throw ioE;
367
        }
368

    
369
        //set up properties
370
        Properties prop = new Properties();
371
        prop.put("action", "update");
372
        prop.put("docid", docid);
373
        prop.put("doctext", doctext);
374
        if (schematext != null) {
375
            prop.put("dtdtext", schematext);
376
        }
377

    
378
        String response = null;
379
        try {
380
            response = sendDataForString(prop, null, null, 0);
381
        } catch (Exception e) {
382
            throw new MetacatInaccessibleException(e.getMessage());
383
        }
384

    
385
        // Check for an error condition
386
        if (response.indexOf("<error>") != -1) {
387
            if (response.indexOf("does not have permission") != -1) {
388
                throw new InsufficientKarmaException(response);
389
            } else {
390
                throw new MetacatException(response);
391
            }
392
        }
393

    
394
        return response;
395
    }
396

    
397
    /**
398
       * Upload a data document into the repository.
399
       *
400
       * @param docid the docid to insert the document
401
       * @param document a Reader for accessing the document to be uploaded
402
       * @return the metacat response message
403
       * @throws InsufficientKarmaException when the user has insufficent rights
404
       *                                    for the operation
405
       * @throws MetacatInaccessibleException when the metacat server can not be
406
       *                                    reached or does not respond
407
       * @throws MetacatException when the metacat server generates another error
408
       * @throws IOException when there is an error reading the xml document
409
       */
410
      public String upload(String docid, File file)
411
          throws InsufficientKarmaException, MetacatException, IOException,
412
          MetacatInaccessibleException
413
      {
414

    
415
          URL url = new URL(metacatUrl.trim());
416
          HttpMessage msg = new HttpMessage(url);
417
          //set up properties
418
          Properties arg = new Properties();
419
          arg.put("action", "upload");
420
          arg.put("docid", docid);
421

    
422
          Properties filenames = new Properties();
423
          String filename = file.getAbsolutePath();
424
          filenames.put("datafile", filename);
425

    
426
          String response = null;
427
          try {
428
            response = sendDataForString(arg, filenames, null, 0);
429
          } catch (Exception e) {
430
            throw new MetacatInaccessibleException(e.getMessage());
431
          }
432

    
433
          // Check for an error condition
434
          if (response.indexOf("<error>") != -1) {
435
            if (response.indexOf("does not have permission") != -1) {
436
              throw new InsufficientKarmaException(response);
437
            } else {
438
              throw new MetacatException(response);
439
            }
440
          }
441

    
442
          return response;
443
      }
444

    
445
      /**
446
          * Upload a data document into the repository.
447
          *
448
          * @param docid the docid to insert the document
449
          * @param document a Reader for accessing the document to be uploaded
450
          * @return the metacat response message
451
          * @throws InsufficientKarmaException when the user has insufficent rights
452
          *                                    for the operation
453
          * @throws MetacatInaccessibleException when the metacat server can not be
454
          *                                    reached or does not respond
455
          * @throws MetacatException when the metacat server generates another error
456
          * @throws IOException when there is an error reading the xml document
457
          */
458

    
459

    
460
      public String upload(String docid, String filename, InputStream fileData,
461
                           int size)
462
          throws InsufficientKarmaException, MetacatException, IOException,
463
          MetacatInaccessibleException {
464

    
465
          URL url = new URL(metacatUrl.trim());
466
          HttpMessage msg = new HttpMessage(url);
467
          //set up properties
468
          Properties arg = new Properties();
469
          arg.put("action", "upload");
470
          arg.put("docid", docid);
471

    
472
          Properties filenames = new Properties();
473
          filenames.put("datafile", filename);
474

    
475
          String response = null;
476
          try {
477
            response = sendDataForString(arg, filenames, fileData, size);
478
          } catch (Exception e) {
479
            throw new MetacatInaccessibleException(e.getMessage());
480
          }
481

    
482
          // Check for an error condition
483
          if (response.indexOf("<error>") != -1) {
484
            if (response.indexOf("does not have permission") != -1) {
485
              throw new InsufficientKarmaException(response);
486
            } else {
487
              throw new MetacatException(response);
488
            }
489
          }
490

    
491
          return response;
492
      }
493

    
494
    /**
495
     * Delete an XML document in the repository.
496
     *
497
     * @param docid the docid to delete
498
     * @return the metacat response message
499
     * @throws InsufficientKarmaException when the user has insufficent rights
500
     *                                    for the operation
501
     * @throws MetacatInaccessibleException when the metacat server can not be
502
     *                                    reached or does not respond
503
     * @throws MetacatException when the metacat server generates another error
504
     */
505
    public String delete(String docid)
506
        throws InsufficientKarmaException, MetacatException,
507
        MetacatInaccessibleException
508
    {
509
        //set up properties
510
        Properties prop = new Properties();
511
        prop.put("action", "delete");
512
        prop.put("docid", docid);
513

    
514
        String response = null;
515
        try {
516
            response = sendDataForString(prop, null, null, 0);
517
        } catch (Exception e) {
518
            throw new MetacatInaccessibleException(e.getMessage());
519
        }
520

    
521
        // Check for an error condition
522
        if (response.indexOf("<error>") != -1) {
523
            if (response.indexOf("does not have permission") != -1) {
524
                throw new InsufficientKarmaException(response);
525
            } else {
526
                throw new MetacatException(response);
527
            }
528
        }
529

    
530
        return response;
531
    }
532

    
533
    /**
534
     * When the MetacatFactory creates an instance it needs to set the
535
     * MetacatUrl to which connections should be made.
536
     *
537
     * @param metacatUrl the URL for the metacat server
538
     */
539
    public void setMetacatUrl(String metacatUrl)
540
    {
541
        this.metacatUrl = metacatUrl;
542
    }
543

    
544
    /**
545
     * Get the session identifier for this session.  This is only valid if
546
     * the login methods has been called successfully for this Metacat object
547
     * beforehand.
548
     *
549
     * @returns the sessionId as a String, or null if the session is invalid
550
     */
551
    public String getSessionId()
552
    {
553
        return this.sessionId;
554
    }
555

    
556
    /**
557
     * Set the session identifier for this session.  This identifier was
558
     * previously established with a call to login.  To continue to use the
559
     * same session, set the session id before making a call to one of the
560
     * metacat access methods (e.g., read, query, insert, etc.).
561
     *
562
     * @param String the sessionId from a previously established session
563
     */
564
    public void setSessionId(String sessionId)
565
    {
566
        this.sessionId = sessionId;
567
    }
568

    
569
    /************************************************************************
570
     * PRIVATE METHODS
571
     ************************************************************************/
572

    
573
    /**
574
     * Send a request to metacat.
575
     *
576
     * @param prop the properties to be URL encoded and sent
577
     * @param filename  the properties to be sent to Metacat
578
     *                  in case of upload, otherwise null
579
     * @param fileData  the inputStream for the file data to be sent to Metacat
580
     *                  in case of upload, otherwise null
581
     * @param size      the size of the data being sent to Metacat
582
     *                  in case of upload, otherwise 0
583
     */
584
    synchronized private InputStream sendDataOnce(Properties args,
585
                                                  Properties filename,
586
                                                  InputStream fileData,
587
                                                  int size)
588
        throws Exception
589
    {
590
        InputStream returnStream = null;
591
        URL url = new URL(metacatUrl);
592
        HttpMessage msg = new HttpMessage(url);
593
        msg.setCookie("JSESSIONID="+this.sessionId);
594
        if (filename == null){
595
            returnStream = msg.sendPostData(args);
596
        } else if (fileData == null){
597
            returnStream = msg.sendPostData(args, filename);
598
        } else if (size > 0) {
599
            returnStream = msg.sendPostData(args, filename, fileData, size);
600
        } else {
601
            throw new MetacatException("Invalid size specified for " +
602
                                       "the input stream being passed");
603
        }
604
        return returnStream;
605
    }
606

    
607
    /**
608
     * Send a request to Metacat
609
     *
610
     * @param args  the properties to be sent to Metacat
611
     * @param filename  the properties to be sent to Metacat
612
     *                  in case of upload, otherwise null
613
     * @param fileData  the inputStream for the file data to be sent to Metacat
614
     *                  in case of upload, otherwise null
615
     * @param size      the size of the data being sent to Metacat
616
     *                  in case of upload, otherwise 0
617
     * @return      InputStream as returned by Metacat
618
     */
619
    synchronized private InputStream sendData(Properties args,
620
                                              Properties filename,
621
                                              InputStream fileData,
622
                                              int size)
623
        throws Exception
624
    {
625
        InputStream returnStream = null;
626

    
627
        /*
628
            Note:  The reason that there are three try statements all executing
629
            the same code is that there is a problem with the initial connection
630
            using the HTTPClient protocol handler.  These try statements make
631
            sure that a connection is made because it gives each connection a
632
            2nd and 3rd chance to work before throwing an error.
633
            THIS IS A TOTAL HACK.  THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
634
            RELEASE OF MORPHO!!!  cwb (7/24/01)
635
          */
636
        try {
637
           return sendDataOnce(args, filename, fileData, size);
638
        } catch (Exception e) {
639
            try {
640
                return sendDataOnce(args, filename, fileData, size);
641
            } catch (Exception e2) {
642
                try {
643
                    return sendDataOnce(args, filename, fileData, size);
644
                } catch (Exception e3) {
645
                    System.err.println(
646
                            "Failed to send data to metacat 3 times.");
647
                    throw e3;
648
                }
649
            }
650
        }
651
    }
652

    
653
    /**
654
     * Send a request to Metacat
655
     *
656
     * @param args      the properties to be sent to Metacat
657
     * @param filename  the properties to be sent to Metacat
658
     *                  in case of upload, otherwise null
659
     * @param fileData  the inputStream for the file data to be sent to Metacat
660
     *                  in case of upload, otherwise null
661
     * @param size      the size of the data being sent to Metacat
662
     *                  in case of upload, otherwise 0
663
     * @return          a string as returned by Metacat
664
     */
665
    synchronized private String sendDataForString(Properties args,
666
                                                  Properties filename,
667
                                                  InputStream fileData,
668
                                                  int size)
669
        throws Exception
670
    {
671
        String response = null;
672

    
673
        try {
674
            InputStreamReader returnStream =
675
                    new InputStreamReader(sendData(args, filename,
676
                                                   fileData, size));
677
            StringWriter sw = new StringWriter();
678
            int len;
679
            char[] characters = new char[512];
680
            while ((len = returnStream.read(characters, 0, 512)) != -1) {
681
                sw.write(characters, 0, len);
682
            }
683
            returnStream.close();
684
            response = sw.toString();
685
            sw.close();
686
        } catch (Exception e) {
687
            throw e;
688
        }
689
        return response;
690
    }
691
}
(4-4/7)