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-08-12 00:24:36 -0700 (Tue, 12 Aug 2003) $'
8
 * '$Revision: 1789 $'
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
    /**
52
     * Constructor to create a new instance. Protected because instances
53
     * should only be created by the factory MetacatFactory.
54
     */
55
    protected MetacatClient()
56
    {
57
    }
58

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

    
79
        String response = null;
80
        try {
81
            response = sendDataForString(prop);
82
        } catch (Exception e) {
83
            throw new MetacatInaccessibleException(e.getMessage());
84
        }
85

    
86
        if (response.indexOf("<login>") == -1) {
87
            HttpMessage.setCookie(null);
88
            throw new MetacatAuthException(response);
89
        }
90
    }
91

    
92
    /**
93
     * Read an XML document from the metacat server session, accessed by docid,
94
     * and returned as a Reader.
95
     *
96
     * @param docid the identifier of the document to be read
97
     * @return a Reader for accessing the document
98
     * @throws InsufficientKarmaException when the user has insufficent rights 
99
     *                                    for the operation
100
     * @throws MetacatInaccessibleException when the metacat server can not be
101
     *                                    reached or does not respond
102
     * @throws MetacatException when the metacat server generates another error
103
     */
104
    public Reader read(String docid) throws InsufficientKarmaException,
105
        MetacatInaccessibleException, MetacatException
106
    {
107
        PushbackReader pbr = null;
108

    
109
        Properties prop = new Properties();
110
        prop.put("action", "read");
111
        prop.put("qformat", "xml");
112
        prop.put("docid", docid);
113

    
114
        InputStream response = null;
115
        try {
116
            response = sendData(prop);
117
        } catch (Exception e) {
118
            throw new MetacatInaccessibleException(e.getMessage());
119
        }
120
  
121
        pbr = new PushbackReader(new InputStreamReader(response), 512);
122
        try {
123
            char[] characters = new char[512];
124
            int len = pbr.read(characters, 0, 512);
125
            StringWriter sw = new StringWriter();
126
            sw.write(characters, 0, len);
127
            String message = sw.toString();
128
            sw.close();
129
            pbr.unread(characters, 0, len);
130

    
131
            if (message.indexOf("<error>") != -1) {
132
                if (message.indexOf("does not have permission") != -1) {
133
                    throw new InsufficientKarmaException(message);
134
                } else {
135
                    throw new MetacatException(message);
136
                }
137
            }
138
        } catch (IOException ioe) {
139
            throw new MetacatException(
140
                    "MetacatClient: Error converting Reader to String." 
141
                    + ioe.getMessage());
142
        }
143

    
144
        return pbr;
145
    }
146

    
147
    /**
148
     * Query the metacat document store with the given metacat-compatible 
149
     * query document, and return the result set as a Reader.
150
     *
151
     * @param xmlQuery a Reader for accessing the XML version of the query
152
     * @return a Reader for accessing the result set
153
     */
154
    public Reader query(Reader xmlQuery) throws MetacatInaccessibleException,
155
                                                IOException
156
    {
157
        Reader reader = null;
158
        String query = null;
159
        try {
160
          query = IOUtil.getAsString(xmlQuery, true);
161
        } catch (IOException ioE) {
162
          throw ioE;
163
        }
164

    
165
        //set up properties
166
        Properties prop = new Properties();
167
        prop.put("action", "squery");
168
        prop.put("qformat", "xml");
169
        prop.put("query", query);
170
        
171
        InputStream response = null;
172
        try {
173
            response = sendData(prop);
174
        } catch (Exception e) {
175
            throw new MetacatInaccessibleException(e.getMessage());
176
        }
177
        reader = new InputStreamReader(response);
178
        return reader;
179
    }
180

    
181
    /**
182
     * Insert an XML document into the repository.
183
     *
184
     * @param docid the docid to insert the document
185
     * @param xmlDocument a Reader for accessing the XML document to be inserted
186
     * @param schema a Reader for accessing the DTD or XML Schema for 
187
     *               the document
188
     * @return the metacat response message
189
     * @throws InsufficientKarmaException when the user has insufficent rights 
190
     *                                    for the operation
191
     * @throws MetacatInaccessibleException when the metacat server can not be
192
     *                                    reached or does not respond
193
     * @throws MetacatException when the metacat server generates another error
194
     * @throws IOException when there is an error reading the xml document
195
     */
196
    public String insert(String docid, Reader xmlDocument, Reader schema)
197
        throws InsufficientKarmaException, MetacatException, IOException,
198
        MetacatInaccessibleException
199
    {
200
        Reader reader = null;
201
        String doctext = null;
202
        String schematext = null;
203
        try {
204
          doctext = IOUtil.getAsString(xmlDocument, true);
205
          if (schema != null) {
206
              schematext = IOUtil.getAsString(schema, true);
207
          }
208
        } catch (IOException ioE) {
209
          throw ioE;
210
        }
211

    
212
        //set up properties
213
        Properties prop = new Properties();
214
        prop.put("action", "insert");
215
        prop.put("docid", docid);
216
        prop.put("doctext", doctext);
217
        if (schematext != null) {
218
            prop.put("dtdtext", schematext);
219
        }
220
        
221
        String response = null;
222
        try {
223
            response = sendDataForString(prop);
224
        } catch (Exception e) {
225
            throw new MetacatInaccessibleException(e.getMessage());
226
        }
227

    
228
        // Check for an error condition
229
        if (response.indexOf("<error>") != -1) {
230
            if (response.indexOf("does not have permission") != -1) {
231
                throw new InsufficientKarmaException(response);
232
            } else {
233
                throw new MetacatException(response);
234
            }
235
        }
236

    
237
        return response;
238
    }
239

    
240
    /**
241
     * Update an XML document in the repository.
242
     *
243
     * @param docid the docid to update
244
     * @param xmlDocument a Reader for accessing the XML text to be updated
245
     * @param schema a Reader for accessing the DTD or XML Schema for 
246
     *               the document
247
     * @throws InsufficientKarmaException when the user has insufficent rights 
248
     *                                    for the operation
249
     */
250
    public void update(String docid, Reader xmlDocument, Reader schema)
251
        throws InsufficientKarmaException
252
    {
253
    }
254

    
255
    /**
256
     * Delete an XML document in the repository.
257
     *
258
     * @param docid the docid to delete
259
     * @throws InsufficientKarmaException when the user has insufficent rights 
260
     *                                    for the operation
261
     */
262
    public void delete(String docid)
263
        throws InsufficientKarmaException
264
    {
265
    }
266

    
267
    /**
268
     * When the MetacatFactory creates an instance it needs to set the
269
     * MetacatUrl to which connections should be made.
270
     *
271
     * @param metacatUrl the URL for the metacat server
272
     */
273
    public void setMetacatUrl(String metacatUrl)
274
    {
275
        this.metacatUrl = metacatUrl;
276
    }
277

    
278
    /************************************************************************
279
     * PRIVATE METHODS
280
     ************************************************************************/
281

    
282
    /**
283
     * Send a request to metacat.
284
     *
285
     * @param prop the properties to be URL encoded and sent
286
     */
287
    synchronized private InputStream sendDataOnce(Properties prop) 
288
        throws Exception
289
    {
290
        InputStream returnStream = null;
291
        URL url = new URL(metacatUrl);
292
        HttpMessage msg = new HttpMessage(url);
293
        returnStream = msg.sendPostData(prop);
294
        return returnStream;
295
    }
296

    
297
    /**
298
     * Send a request to Metacat
299
     *
300
     * @param prop  the properties to be sent to Metacat
301
     * @return      InputStream as returned by Metacat
302
     */
303
    synchronized private InputStream sendData(Properties prop) throws Exception
304
    {   
305
        InputStream returnStream = null;
306

    
307
        /*
308
            Note:  The reason that there are three try statements all executing
309
            the same code is that there is a problem with the initial connection
310
            using the HTTPClient protocol handler.  These try statements make 
311
            sure that a connection is made because it gives each connection a 
312
            2nd and 3rd chance to work before throwing an error.
313
            THIS IS A TOTAL HACK.  THIS NEEDS TO BE LOOKED INTO AFTER THE BETA1
314
            RELEASE OF MORPHO!!!  cwb (7/24/01)
315
          */
316
        try {
317
           return sendDataOnce(prop);
318
        } catch (Exception e) {
319
            try {
320
                return sendDataOnce(prop);
321
            } catch (Exception e2) {
322
                try {
323
                    return sendDataOnce(prop);
324
                } catch (Exception e3) {
325
                    System.err.println(
326
                            "Failed to send data to metacat 3 times.");
327
                    throw e3;
328
                }
329
            }
330
        }
331
    }
332

    
333
    /**
334
     * Send a request to Metacat
335
     *
336
     * @param prop  the properties to be sent to Metacat
337
     * @return      a string as returned by Metacat
338
     */
339
    synchronized private String sendDataForString(Properties prop) 
340
        throws Exception
341
    {
342
        String response = null;
343

    
344
        try {
345
            InputStreamReader returnStream =
346
                    new InputStreamReader(sendData(prop));
347
            StringWriter sw = new StringWriter();
348
            int len;
349
            char[] characters = new char[512];
350
            while ((len = returnStream.read(characters, 0, 512)) != -1) {
351
                sw.write(characters, 0, len);
352
            }
353
            returnStream.close();
354
            response = sw.toString();
355
            sw.close();
356
        } catch (Exception e) {
357
            throw e;
358
        }
359
        return response;
360
    }
361
}
(4-4/7)