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: jones $'
7
 *     '$Date: 2003-09-17 12:42:42 -0700 (Wed, 17 Sep 2003) $'
8
 * '$Revision: 1822 $'
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

    
40

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

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

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

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

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

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

    
105
    /**
106
     *  Method used to log out a metacat server. The Metacat server will end
107
     *  the session when this call is invoked.
108
     *
109
     *  @return the response string from metacat in XML format
110
     *  @throws MetacatInaccessibleException when the metacat server can not be
111
     *                                    reached or does not respond
112
     */
113
    public String logout() throws MetacatInaccessibleException, MetacatException
114
    {
115
        Properties prop = new Properties();
116
        prop.put("action", "logout");
117
        prop.put("qformat", "xml"); 
118
       
119
        String response = null;
120
        try {
121
            response = sendDataForString(prop);
122
        } catch (Exception e) {
123
            throw new MetacatInaccessibleException(e.getMessage());
124
        }
125
        
126
        if (response.indexOf("<logout>") == -1) {
127
            throw new MetacatException(response);
128
        }
129
        this.sessionId = null;
130
        return response;
131
    }
132
    
133
    /**
134
     * Read an XML document from the metacat server session, accessed by docid,
135
     * and returned as a Reader.
136
     *
137
     * @param docid the identifier of the document to be read
138
     * @return a Reader for accessing the document
139
     * @throws InsufficientKarmaException when the user has insufficent rights 
140
     *                                    for the operation
141
     * @throws MetacatInaccessibleException when the metacat server can not be
142
     *                                    reached or does not respond
143
     * @throws MetacatException when the metacat server generates another error
144
     */
145
    public Reader read(String docid) throws InsufficientKarmaException,
146
        MetacatInaccessibleException, MetacatException
147
    {
148
        PushbackReader pbr = null;
149

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

    
155
        InputStream response = null;
156
        try {
157
            response = sendData(prop);
158
        } catch (Exception e) {
159
            throw new MetacatInaccessibleException(e.getMessage());
160
        }
161
  
162
        pbr = new PushbackReader(new InputStreamReader(response), 512);
163
        try {
164
            char[] characters = new char[512];
165
            int len = pbr.read(characters, 0, 512);
166
            StringWriter sw = new StringWriter();
167
            sw.write(characters, 0, len);
168
            String message = sw.toString();
169
            sw.close();
170
            pbr.unread(characters, 0, len);
171

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

    
185
        return pbr;
186
    }
187

    
188
    /**
189
     * Query the metacat document store with the given metacat-compatible 
190
     * query document, and return the result set as a Reader.
191
     *
192
     * @param xmlQuery a Reader for accessing the XML version of the query
193
     * @return a Reader for accessing the result set
194
     */
195
    public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
196
                                                IOException
197
    {
198
        Reader reader = null;
199
        String query = null;
200
        try {
201
          query = IOUtil.getAsString(xmlQuery, true);
202
        } catch (IOException ioE) {
203
          throw ioE;
204
        }
205

    
206
        //set up properties
207
        Properties prop = new Properties();
208
        prop.put("action", "squery");
209
        prop.put("qformat", "xml");
210
        prop.put("query", query);
211
        
212
        InputStream response = null;
213
        try {
214
            response = sendData(prop);
215
        } catch (Exception e) {
216
            throw new MetacatInaccessibleException(e.getMessage());
217
        }
218
        reader = new InputStreamReader(response);
219
        return reader;
220
    }
221

    
222
    /**
223
     * Insert an XML document into the repository.
224
     *
225
     * @param docid the docid to insert the document
226
     * @param xmlDocument a Reader for accessing the XML document to be inserted
227
     * @param schema a Reader for accessing the DTD or XML Schema for 
228
     *               the document
229
     * @return the metacat response message
230
     * @throws InsufficientKarmaException when the user has insufficent rights 
231
     *                                    for the operation
232
     * @throws MetacatInaccessibleException when the metacat server can not be
233
     *                                    reached or does not respond
234
     * @throws MetacatException when the metacat server generates another error
235
     * @throws IOException when there is an error reading the xml document
236
     */
237
    public String insert(String docid, Reader xmlDocument, Reader schema)
238
        throws InsufficientKarmaException, MetacatException, IOException,
239
        MetacatInaccessibleException
240
    {
241
        Reader reader = null;
242
        String doctext = null;
243
        String schematext = null;
244
        try {
245
          doctext = IOUtil.getAsString(xmlDocument, true);
246
          if (schema != null) {
247
              schematext = IOUtil.getAsString(schema, true);
248
          }
249
        } catch (IOException ioE) {
250
          throw ioE;
251
        }
252

    
253
        //set up properties
254
        Properties prop = new Properties();
255
        prop.put("action", "insert");
256
        prop.put("docid", docid);
257
        prop.put("doctext", doctext);
258
        if (schematext != null) {
259
            prop.put("dtdtext", schematext);
260
        }
261
        
262
        String response = null;
263
        try {
264
            response = sendDataForString(prop);
265
        } catch (Exception e) {
266
            throw new MetacatInaccessibleException(e.getMessage());
267
        }
268

    
269
        // Check for an error condition
270
        if (response.indexOf("<error>") != -1) {
271
            if (response.indexOf("does not have permission") != -1) {
272
                throw new InsufficientKarmaException(response);
273
            } else {
274
                throw new MetacatException(response);
275
            }
276
        }
277

    
278
        return response;
279
    }
280

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

    
312
        //set up properties
313
        Properties prop = new Properties();
314
        prop.put("action", "update");
315
        prop.put("docid", docid);
316
        prop.put("doctext", doctext);
317
        if (schematext != null) {
318
            prop.put("dtdtext", schematext);
319
        }
320
        
321
        String response = null;
322
        try {
323
            response = sendDataForString(prop);
324
        } catch (Exception e) {
325
            throw new MetacatInaccessibleException(e.getMessage());
326
        }
327

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

    
337
        return response;
338
    }
339

    
340
    /**
341
     * Delete an XML document in the repository.
342
     *
343
     * @param docid the docid to delete
344
     * @return the metacat response message
345
     * @throws InsufficientKarmaException when the user has insufficent rights 
346
     *                                    for the operation
347
     * @throws MetacatInaccessibleException when the metacat server can not be
348
     *                                    reached or does not respond
349
     * @throws MetacatException when the metacat server generates another error
350
     */
351
    public String delete(String docid)
352
        throws InsufficientKarmaException, MetacatException,
353
        MetacatInaccessibleException
354
    {
355
        //set up properties
356
        Properties prop = new Properties();
357
        prop.put("action", "delete");
358
        prop.put("docid", docid);
359
        
360
        String response = null;
361
        try {
362
            response = sendDataForString(prop);
363
        } catch (Exception e) {
364
            throw new MetacatInaccessibleException(e.getMessage());
365
        }
366

    
367
        // Check for an error condition
368
        if (response.indexOf("<error>") != -1) {
369
            if (response.indexOf("does not have permission") != -1) {
370
                throw new InsufficientKarmaException(response);
371
            } else {
372
                throw new MetacatException(response);
373
            }
374
        }
375

    
376
        return response;
377
    }
378

    
379
    /**
380
     * When the MetacatFactory creates an instance it needs to set the
381
     * MetacatUrl to which connections should be made.
382
     *
383
     * @param metacatUrl the URL for the metacat server
384
     */
385
    public void setMetacatUrl(String metacatUrl)
386
    {
387
        this.metacatUrl = metacatUrl;
388
    }
389

    
390
    /**
391
     * Get the session identifier for this session.  This is only valid if
392
     * the login methods has been called successfully for this Metacat object
393
     * beforehand.
394
     *
395
     * @returns the sessionId as a String, or null if the session is invalid
396
     */
397
    public String getSessionId()
398
    {
399
        return this.sessionId;
400
    }
401

    
402
    /************************************************************************
403
     * PRIVATE METHODS
404
     ************************************************************************/
405

    
406
    /**
407
     * Send a request to metacat.
408
     *
409
     * @param prop the properties to be URL encoded and sent
410
     */
411
    synchronized private InputStream sendDataOnce(Properties prop) 
412
        throws Exception
413
    {
414
        InputStream returnStream = null;
415
        URL url = new URL(metacatUrl);
416
        HttpMessage msg = new HttpMessage(url);
417
        returnStream = msg.sendPostData(prop);
418
        return returnStream;
419
    }
420

    
421
    /**
422
     * Send a request to Metacat
423
     *
424
     * @param prop  the properties to be sent to Metacat
425
     * @return      InputStream as returned by Metacat
426
     */
427
    synchronized private InputStream sendData(Properties prop) throws Exception
428
    {   
429
        InputStream returnStream = null;
430

    
431
        /*
432
            Note:  The reason that there are three try statements all executing
433
            the same code is that there is a problem with the initial connection
434
            using the HTTPClient protocol handler.  These try statements make 
435
            sure that a connection is made because it gives each connection a 
436
            2nd and 3rd chance to work before throwing an error.
437
            THIS IS A TOTAL HACK.  THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
438
            RELEASE OF MORPHO!!!  cwb (7/24/01)
439
          */
440
        try {
441
           return sendDataOnce(prop);
442
        } catch (Exception e) {
443
            try {
444
                return sendDataOnce(prop);
445
            } catch (Exception e2) {
446
                try {
447
                    return sendDataOnce(prop);
448
                } catch (Exception e3) {
449
                    System.err.println(
450
                            "Failed to send data to metacat 3 times.");
451
                    throw e3;
452
                }
453
            }
454
        }
455
    }
456

    
457
    /**
458
     * Send a request to Metacat
459
     *
460
     * @param prop  the properties to be sent to Metacat
461
     * @return      a string as returned by Metacat
462
     */
463
    synchronized private String sendDataForString(Properties prop) 
464
        throws Exception
465
    {
466
        String response = null;
467

    
468
        try {
469
            InputStreamReader returnStream =
470
                    new InputStreamReader(sendData(prop));
471
            StringWriter sw = new StringWriter();
472
            int len;
473
            char[] characters = new char[512];
474
            while ((len = returnStream.read(characters, 0, 512)) != -1) {
475
                sw.write(characters, 0, len);
476
            }
477
            returnStream.close();
478
            response = sw.toString();
479
            sw.close();
480
        } catch (Exception e) {
481
            throw e;
482
        }
483
        return response;
484
    }
485
}
(4-4/7)