Project

General

Profile

1 1780 jones
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author$'
7
 *     '$Date$'
8
 * '$Revision$'
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 1786 tao
import java.io.BufferedReader;
28 1780 jones
import java.io.InputStream;
29 1783 jones
import java.io.InputStreamReader;
30 1784 jones
import java.io.PushbackReader;
31
import java.io.IOException;
32 1783 jones
import java.io.StringWriter;
33 1780 jones
import java.io.Reader;
34
import java.net.URL;
35
import java.util.Properties;
36
37
import edu.ucsb.nceas.utilities.HttpMessage;
38 1788 jones
import edu.ucsb.nceas.utilities.IOUtil;
39 1780 jones
40 1788 jones
41 1780 jones
/**
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 1822 jones
    /** The session identifier for the session */
52
    private String sessionId;
53
54 1780 jones
    /**
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 1822 jones
        this.metacatUrl = null;
61
        this.sessionId = null;
62 1780 jones
    }
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 1822 jones
     *  @return the response string from metacat in XML format
73 1780 jones
     *  @throws MetacatAuthException when the username/password could
74
     *                    not be authenticated
75
     */
76 1822 jones
    public String login(String username, String password)
77 1780 jones
           throws MetacatAuthException, MetacatInaccessibleException
78
    {
79 1783 jones
        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 1828 jones
            setSessionId("");
94 1783 jones
            throw new MetacatAuthException(response);
95 1822 jones
        } else {
96 1825 jones
            int start = response.indexOf("<sessionId>") + 11;
97 1822 jones
            int end = response.indexOf("</sessionId>");
98
            if ((start != -1) && (end != -1)) {
99 1828 jones
                setSessionId(response.substring(start,end));
100 1822 jones
            }
101 1783 jones
        }
102 1822 jones
        return response;
103 1780 jones
    }
104
105
    /**
106 1822 jones
     *  Method used to log out a metacat server. The Metacat server will end
107
     *  the session when this call is invoked.
108 1798 tao
     *
109 1822 jones
     *  @return the response string from metacat in XML format
110 1798 tao
     *  @throws MetacatInaccessibleException when the metacat server can not be
111
     *                                    reached or does not respond
112
     */
113 1822 jones
    public String logout() throws MetacatInaccessibleException, MetacatException
114 1798 tao
    {
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 1828 jones
        setSessionId("");
130 1822 jones
        return response;
131 1798 tao
    }
132
133
    /**
134 1780 jones
     * 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 1784 jones
     * @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 1780 jones
     */
145 1784 jones
    public Reader read(String docid) throws InsufficientKarmaException,
146
        MetacatInaccessibleException, MetacatException
147 1780 jones
    {
148 1784 jones
        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 1780 jones
    }
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 1786 tao
    public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
196
                                                IOException
197 1780 jones
    {
198 1786 tao
        Reader reader = null;
199
        String query = null;
200 1788 jones
        try {
201
          query = IOUtil.getAsString(xmlQuery, true);
202
        } catch (IOException ioE) {
203 1786 tao
          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 1780 jones
    }
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 1789 jones
     * @return the metacat response message
230 1780 jones
     * @throws InsufficientKarmaException when the user has insufficent rights
231
     *                                    for the operation
232 1789 jones
     * @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 1780 jones
     */
237 1789 jones
    public String insert(String docid, Reader xmlDocument, Reader schema)
238
        throws InsufficientKarmaException, MetacatException, IOException,
239
        MetacatInaccessibleException
240 1780 jones
    {
241 1789 jones
        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 1780 jones
    }
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 1795 jones
     * @return the metacat response message
289 1780 jones
     * @throws InsufficientKarmaException when the user has insufficent rights
290
     *                                    for the operation
291 1795 jones
     * @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 1780 jones
     */
296 1795 jones
    public String update(String docid, Reader xmlDocument, Reader schema)
297
        throws InsufficientKarmaException, MetacatException, IOException,
298
        MetacatInaccessibleException
299 1780 jones
    {
300 1795 jones
        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 1780 jones
    }
339
340
    /**
341
     * Delete an XML document in the repository.
342
     *
343
     * @param docid the docid to delete
344 1795 jones
     * @return the metacat response message
345 1780 jones
     * @throws InsufficientKarmaException when the user has insufficent rights
346
     *                                    for the operation
347 1795 jones
     * @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 1780 jones
     */
351 1795 jones
    public String delete(String docid)
352
        throws InsufficientKarmaException, MetacatException,
353
        MetacatInaccessibleException
354 1780 jones
    {
355 1795 jones
        //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 1780 jones
    }
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 1783 jones
        this.metacatUrl = metacatUrl;
388 1780 jones
    }
389
390 1822 jones
    /**
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 1826 jones
    /**
403
     * Set the session identifier for this session.  This identifier was
404
     * previously established with a call to login.  To continue to use the
405
     * same session, set the session id before making a call to one of the
406
     * metacat access methods (e.g., read, query, insert, etc.).
407
     *
408
     * @param String the sessionId from a previously established session
409
     */
410
    public void setSessionId(String sessionId)
411
    {
412
        this.sessionId = sessionId;
413
    }
414
415 1780 jones
    /************************************************************************
416
     * PRIVATE METHODS
417
     ************************************************************************/
418
419 1783 jones
    /**
420
     * Send a request to metacat.
421
     *
422
     * @param prop the properties to be URL encoded and sent
423
     */
424 1780 jones
    synchronized private InputStream sendDataOnce(Properties prop)
425
        throws Exception
426
    {
427
        InputStream returnStream = null;
428
        URL url = new URL(metacatUrl);
429
        HttpMessage msg = new HttpMessage(url);
430 1828 jones
        msg.setCookie("JSESSIONID="+this.sessionId);
431 1780 jones
        returnStream = msg.sendPostData(prop);
432
        return returnStream;
433
    }
434
435
    /**
436
     * Send a request to Metacat
437
     *
438
     * @param prop  the properties to be sent to Metacat
439
     * @return      InputStream as returned by Metacat
440
     */
441
    synchronized private InputStream sendData(Properties prop) throws Exception
442
    {
443
        InputStream returnStream = null;
444
445
        /*
446
            Note:  The reason that there are three try statements all executing
447
            the same code is that there is a problem with the initial connection
448
            using the HTTPClient protocol handler.  These try statements make
449
            sure that a connection is made because it gives each connection a
450
            2nd and 3rd chance to work before throwing an error.
451
            THIS IS A TOTAL HACK.  THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
452
            RELEASE OF MORPHO!!!  cwb (7/24/01)
453
          */
454
        try {
455
           return sendDataOnce(prop);
456
        } catch (Exception e) {
457
            try {
458
                return sendDataOnce(prop);
459
            } catch (Exception e2) {
460
                try {
461
                    return sendDataOnce(prop);
462
                } catch (Exception e3) {
463
                    System.err.println(
464
                            "Failed to send data to metacat 3 times.");
465
                    throw e3;
466
                }
467
            }
468
        }
469
    }
470 1783 jones
471
    /**
472
     * Send a request to Metacat
473
     *
474
     * @param prop  the properties to be sent to Metacat
475
     * @return      a string as returned by Metacat
476
     */
477
    synchronized private String sendDataForString(Properties prop)
478
        throws Exception
479
    {
480
        String response = null;
481
482
        try {
483
            InputStreamReader returnStream =
484
                    new InputStreamReader(sendData(prop));
485
            StringWriter sw = new StringWriter();
486
            int len;
487
            char[] characters = new char[512];
488
            while ((len = returnStream.read(characters, 0, 512)) != -1) {
489
                sw.write(characters, 0, len);
490
            }
491
            returnStream.close();
492
            response = sw.toString();
493
            sw.close();
494
        } catch (Exception e) {
495
            throw e;
496
        }
497
        return response;
498
    }
499 1780 jones
}