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: Serhan AKIN $'
7
 *     '$Date: 2009-06-13 15:28:13 +0300  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.restservice;
24

    
25
import java.io.BufferedReader;
26
import java.io.IOException;
27
import java.io.PrintWriter;
28
import java.util.Enumeration;
29
import java.util.Hashtable;
30
import java.util.Timer;
31

    
32
import javax.servlet.ServletContext;
33
import javax.servlet.http.HttpServletRequest;
34
import javax.servlet.http.HttpServletResponse;
35

    
36
import org.apache.log4j.Logger;
37

    
38
import edu.ucsb.nceas.metacat.DBUtil;
39
import edu.ucsb.nceas.metacat.IdentifierManager;
40
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
41
import edu.ucsb.nceas.metacat.MetacatHandler;
42
import edu.ucsb.nceas.metacat.util.RequestUtil;
43
import edu.ucsb.nceas.metacat.util.SessionData;
44

    
45
/**
46
 * 
47
 * Implements Earthgrid REST API to Metacat <br/><br/> 
48
 * 
49
 * <ul>
50
 * <li>
51
 * <h3> EarthGrid Query Service</h3>
52
 * <ul><li>
53
 * <h3>Get & Authenticated Get:</h3> 
54
 * is equal to Metacat's read action and returns a data file having
55
 * the specified <doc-id> in the resource path. For authenticated Get service, a session id must be provided 
56
 * in the query string. <br/><br/>
57
 * 
58
 * <b>REST URL:</b>	<code>GET, [context-root]/object/[doc-id]?sessionid=[sessionid] </code><br/>
59
 * <b>Returns:</b> data file <br/><br/>
60
 * </li>
61
 * 
62
 * <li>
63
 * <h3>Query & Authenticated Query:</h3> 
64
 * Metacat's equivalent is squery action but this function 
65
 * receives a Earthgrid query document and returns Earthgrid resultset document by transforming those documents
66
 * to Metacat's equivalents by the means of Metacat Implementation in Earthgrid library. For authenticated Query service 
67
 * a session id must be provided in the query string. See Earthgrid (a.k.a. Ecogrid) project for XSD files of 
68
 * query and resultset documents<br/><br/>
69
 * 
70
 * <b>REST URL:</b>	<code>POST, [context-root]/object?sessionid=[sessionid]</code>    <br/>
71
 * <b>POST Data:</b> Earthgrid query document , Content-type: <code>text/xml</code><br/>
72
 * <b>Returns:</b> Earthgrid resultset document<br/><br/>
73
 * 
74
 * </li>
75
 * </ul>
76
 * 
77
 * </li>
78
 * 
79
 * <li>
80
 * <h3> EarthGrid Authentication Service</h3>
81
 * <ul><li>
82
 * <h3>Login: </h3> 
83
 * Receives username and password parameters in POST data and 
84
 * returns SessionId (in XML format) or failure message and uses MetacatHandler's handleLoginAction function<br/><br/>
85
 *  
86
 * <b>REST URL:</b> <code>POST, [context-root]/session?op=login</code> <br/>
87
 * <b>POST Data:</b> username=[username]&password=[password], Content-type: <code>application/x-www-form-urlencoded</code>
88
 * <b>Returns:</b> sessionId in XML format<br/><br/>
89
 * </li>
90
 * 
91
 * <li>
92
 * <h3>Logout: </h3> 
93
 * Receives session Id parameters in querystring and returns xml message, calls 
94
 * MetacatHandler's handleLogoutAction function<br/><br/>
95
 *  
96
 * <b>REST URL:</b>	<code>GET, [context-root]/session?op=logout&sessionid=[sessionid]</code>   <br/>
97
 * <b>Returns:</b> message in XML format<br/><br/>
98
 * </li>
99
 * </ul>
100
 * 
101
 * <li>
102
 * <h3>EarthGrid Put Service</h3>
103
 * 
104
 * <ul>
105
 * <li><h3>Update/Insert: </h3>		
106
 * <br/>
107
 * <b>REST URL:</b>	<code>PUT, [context-root]/object/[doc-id]?op={update|insert}&sessionid=[sessionid]</code>   <br/>
108
 * <b>POST Data:</b> document object, Content-type: <code>text/xml</code><br/>
109
 * <b>Returns:</b> message in XML format<br/><br/>
110
 * </li>
111
 * 
112
 * <li><h3>Delete: </h3>		
113
 * <br/>
114
 * <b>REST URL:</b>	<code>DELETE, [context-root]/object/[doc-id]?sessionid=[sessionid]</code>   <br/>
115
 * <b>Returns:</b> message in XML format<br/><br/>
116
 * </li>
117

    
118
 * </ul>
119
 * </li>
120
 * 
121
 * <li>
122
 * <h3>EarthGrid Identifier Service</h3><br/>
123
 * 
124
 * <ul>
125
 * <li><h3>isRegistered: </h3>		<br/>
126
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=isregistered</code>   <br/>
127
 * <b>Returns:</b> message in XML format<br/><br/>
128
 * </li>
129

    
130
 * <li><h3>getAllDocIds:</h3>		<br/>		
131
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getalldocids</code>   <br/>
132
 * <b>Returns:</b> document id list in XML format<br/><br/>
133
 * </li>
134
 * 
135
 * <li><h3>addLSID Function:</h3> 
136
 * Metacat does not support this function 		<br/>
137
 * <b>REST URL:</b>	<code>PUT, [context-root]/identifier/[doc-id]</code>   <br/>
138
 * <b>Returns:</b> error message in XML format<br/><br/>
139
 * </li>
140
 * 
141
 * <li><h3>getNextRevision:</h3>		<br/>
142
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=getnextrevision</code>   <br/>
143
 * <b>Returns:</b> message in XML format<br/><br/>
144
 * </li>
145
 * 
146
 * <li><h3>getNextObject:</h3>		<br/>
147
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getnextobject&scope=[scope]</code>   <br/>
148
 * <b>Returns:</b> message in XML format<br/><br/>
149
 * </li>
150
 * 
151
 * </li>
152
 * </ul>
153
 * 
154
 */
155
public class ResourceHandler {
156

    
157
    /**HTTP Verb GET*/
158
    public static final byte GET = 1;
159
    /**HTTP Verb POST*/
160
    public static final byte POST = 2;
161
    /**HTTP Verb PUT*/
162
    public static final byte PUT = 3;
163
    /**HTTP Verb DELETE*/
164
    public static final byte DELETE = 4;
165

    
166
    /*
167
     * API Resources
168
     */
169
    private static final String RESOURCE_OBJECTS = "object";
170
    private static final String RESOURCE_SESSION = "session";
171
    private static final String RESOURCE_IDENTIFIER = "identifier";
172

    
173
    /*
174
     * API Functions used as URL parameters
175
     */
176
    private static final String FUNCTION_KEYWORD = "op";
177
    private static final String FUNCTION_NAME_LOGIN = "login";
178
    private static final String FUNCTION_NAME_LOGOUT = "logout";
179
    private static final String FUNCTION_NAME_ISREGISTERED = "isregistered";
180
    private static final String FUNCTION_NAME_GETALLDOCS = "getalldocids";
181
    private static final String FUNCTION_NAME_GETNEXTREV = "getnextrevision";
182
    private static final String FUNCTION_NAME_GETNEXTOBJ = "getnextobject";
183
    private static final String FUNCTION_NAME_INSERT = "insert";
184
    private static final String FUNCTION_NAME_UPDATE = "update";
185

    
186
    private ServletContext servletContext;
187
    private Logger logMetacat;
188
    private MetacatHandler handler;
189
    private HttpServletRequest request;
190
    private HttpServletResponse response;
191
    private String username;
192
    private String password;
193
    private String sessionId;
194
    private String[] groupNames;
195

    
196
    private Hashtable<String, String[]> params;
197

    
198
    /**Initializes new instance by setting servlet context,request and response*/
199
    public ResourceHandler(ServletContext servletContext,
200
            HttpServletRequest request, HttpServletResponse response) {
201
        this.servletContext = servletContext;
202
        this.request = request;
203
        this.response = response;
204
    }
205

    
206
    /**
207
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions  
208
     */
209
    private void initParams() {
210

    
211
        String name = null;
212
        String[] value = null;
213
        Enumeration paramlist = request.getParameterNames();
214
        while (paramlist.hasMoreElements()) {
215
            name = (String) paramlist.nextElement();
216
            value = request.getParameterValues(name);
217
            params.put(name, value);
218
        }
219

    
220
    }
221

    
222
    /**
223
     * 
224
     * Load user details of metacat session from the request 
225
     * 
226
     */
227
    private void loadSessionData() {
228
        SessionData sessionData = RequestUtil.getSessionData(request);
229

    
230
        username = sessionData.getUserName();
231
        password = sessionData.getPassword();
232
        groupNames = sessionData.getGroupNames();
233
        sessionId = sessionData.getId();
234

    
235
        if (username == null)
236
            username = "public";
237
    }
238

    
239
    /**
240
     * This function is called from REST APU servlet and handles each request to the servlet 
241
     * 
242
     * @param httpVerb (GET, POST, PUT or DELETE)
243
     */
244
    public void handle(byte httpVerb) {
245

    
246
        logMetacat = Logger.getLogger(ResourceHandler.class);
247
        try {
248
            String resource = request.getServletPath();
249

    
250
            boolean status = false;
251

    
252
            if (resource != null) {
253
                resource = request.getServletPath().substring(1);
254
                System.out.println("accessing resource: " + resource);
255

    
256
                params = new Hashtable<String, String[]>();
257
                initParams();
258

    
259
                Timer timer = new Timer();
260
                handler = new MetacatHandler(servletContext, timer);
261

    
262
                if (resource.equals(RESOURCE_SESSION) && httpVerb == POST
263
                        && params.get(FUNCTION_KEYWORD) != null) {
264
                    if (params.get(FUNCTION_KEYWORD)[0]
265
                            .equals(FUNCTION_NAME_LOGIN)) {
266
                        login();
267
                        status = true;
268
                    } else if (params.get(FUNCTION_KEYWORD)[0]
269
                            .equals(FUNCTION_NAME_LOGOUT)) {
270
                        logout();
271
                        status = true;
272
                    }
273
                } else if (resource.equals(RESOURCE_OBJECTS)) {
274

    
275
                    loadSessionData();
276

    
277
                    String objectId = request.getPathInfo();
278
                    if (objectId != null && objectId.length() > 1)
279
                        objectId = request.getPathInfo().substring(1); //trim the slash
280

    
281
                    System.out.println("verb:" + httpVerb);
282

    
283
                    System.out.println("objectId:" + objectId);
284

    
285
                    if (httpVerb == GET) {
286
                        getObject(objectId);
287
                        status = true;
288
                    } else if (httpVerb == POST) {
289
                        query();
290
                        status = true;
291
                    } else if (httpVerb == PUT) {
292
                        putObject(objectId);
293
                        status = true;
294
                    } else if (httpVerb == DELETE) {
295
                        deleteObject(objectId);
296
                        status = true;
297
                    }
298

    
299
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
300

    
301
                    String identifierId = request.getPathInfo();
302
                    if (identifierId != null && identifierId.length() > 1)
303
                        identifierId = request.getPathInfo().substring(1); //trim the slash
304

    
305
                    System.out.println("identifierId:" + identifierId);
306

    
307
                    if (httpVerb == GET) {
308
                        String op = params.get(FUNCTION_KEYWORD)[0];
309
                        System.out.println("op:" + op);
310
                        if (op.equals(FUNCTION_NAME_ISREGISTERED)) {
311
                            isRegistered(identifierId);
312
                            status = true;
313
                        } else if (op.equals(FUNCTION_NAME_GETALLDOCS)) {
314
                            getAllDocIds();
315
                            status = true;
316
                        } else if (op.equals(FUNCTION_NAME_GETNEXTREV)) {
317
                            getNextRevision(identifierId);
318
                            status = true;
319
                        } else if (op.equals(FUNCTION_NAME_GETNEXTOBJ)) {
320
                            getNextObject();
321
                            status = true;
322
                        }
323

    
324
                    } else if (httpVerb == PUT) {
325
                        //Earthgrid API > Identifier Service > addLSID Function 
326
                        printError(
327
                                "This method is not supported by metacat.  To "
328
                                        + "add a new LSID, add a document to metacat.",
329
                                response);
330
                        status = true;
331
                    }
332

    
333
                }
334
                if (!status)
335
                    printError("Incorrect parameters!", response);
336
            } else {
337
                printError("Incorrect resource!", response);
338
            }
339
        } catch (Exception e) {
340
            logMetacat.error(e.getMessage());
341
            e.printStackTrace();
342
        }
343
    }
344

    
345
    /**
346
     *  Earthgrid API > Identifier Service > isRegistered Function : calls MetacatHandler > handleIdIsRegisteredAction
347
     * @param identifierId
348
     * @throws IOException
349
     */
350
    private void isRegistered(String identifierId) throws IOException {
351
        params.put("docid", new String[] { identifierId });
352
        PrintWriter out = response.getWriter();
353
        handler.handleIdIsRegisteredAction(out, params, response);
354
        out.close();
355
    }
356

    
357
    /**
358
     * Earthgrid API > Identifier Service > getAllDocIds Function : calls MetacatHandler > handleGetAllDocidsAction
359
     * @throws IOException
360
     */
361
    private void getAllDocIds() throws IOException {
362
        PrintWriter out = response.getWriter();
363
        handler.handleGetAllDocidsAction(out, params, response);
364
        out.close();
365
    }
366

    
367
    /**
368
     * Earthgrid API > Identifier Service > getNextRevision Function : calls MetacatHandler > handleGetRevisionAndDocTypeAction
369
     * @param identifierId
370
     * @throws IOException
371
     */
372
    private void getNextRevision(String identifierId) throws IOException {
373
        params.put("docid", new String[] { identifierId });
374
        PrintWriter out = response.getWriter();
375
        //handler.handleGetRevisionAndDocTypeAction(out, params);
376

    
377
        try {
378
            // Make sure there is a docid
379
            if (identifierId == null || identifierId.equals("")) {
380
                throw new Exception("User didn't specify docid!");
381
            }//if
382

    
383
            // Create a DBUtil object
384
            DBUtil dbutil = new DBUtil();
385
            // Get a rev and doctype
386
            String revAndDocType = dbutil
387
                    .getCurrentRevisionAndDocTypeForGivenDocument(identifierId);
388
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
389

    
390
            out.println("<?xml version=\"1.0\"?>");
391
            out.print("<next-revision>");
392
            out.print(revision);
393
            out.print("</next-revision>");
394

    
395
        } catch (Exception e) {
396
            // Handle exception
397
            out.println("<?xml version=\"1.0\"?>");
398
            out.println("<error>");
399
            out.println(e.getMessage());
400
            out.println("</error>");
401
        }
402

    
403
        out.close();
404
    }
405

    
406
    /**
407
     * Earthgrid API > Identifier Service > getNextObject Function : calls MetacatHandler > handleGetMaxDocidAction
408
     * @throws IOException
409
     */
410
    private void getNextObject() throws IOException {
411
        PrintWriter out = response.getWriter();
412
        handler.handleGetMaxDocidAction(out, params, response);
413
        out.close();
414
    }
415

    
416
    /**
417
     * Earthgrid API > Query Service > Get Function : calls MetacatHandler > handleReadAction
418
     * 
419
     * @param guid ID of data object to be read
420
     */
421
    private void getObject(String guid) {
422
        // Look up the localId for this global identifier
423
        IdentifierManager im = IdentifierManager.getInstance();
424
        String localId = "";
425
        try {
426
            localId = im.getLocalId(guid);
427
        } catch (McdbDocNotFoundException e) {
428
            // Need to return the proper DataONE exception
429
        }
430
        
431
        // Now use that localId to read the object and return it
432
        params.put("docid", new String[] { localId });
433
        handler.handleReadAction(params, request, response, username, password,
434
                groupNames);
435
    }
436

    
437
    /**
438
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query 
439
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
440
     * 
441
     * NOTE:
442
     *      This is the only method that uses EcoGrid classes for its implementation.  
443
     *      It does so because it takes an EcoGrid Query as input, and outputs an
444
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
445
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
446
     *      This creates a circular dependency, because the Metacat classes are needed
447
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
448
     *      method.  This circularity could be resolved by moving the EcoGrid classes
449
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
450
     *      favor of these REST interfaces, this circular dependency can be eliminated.
451
     *        
452
     * @throws Exception
453
     */
454
    private void query() throws Exception {
455
        /*  This block commented out because of the EcoGrid circular dependency.
456
         *  For now, query will not be supported until the circularity can be
457
         *  resolved, probably by movind the ecogrid query syntax transformers
458
         *  directly into the Metacat codebase.  MBJ 2010-02-03
459
         
460
        try {
461
            EcogridQueryParser parser = new EcogridQueryParser(request
462
                    .getReader());
463
            parser.parseXML();
464
            QueryType queryType = parser.getEcogridQuery();
465
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
466
                new EcogridJavaToMetacatJavaQueryTransformer();
467
            QuerySpecification metacatQuery = queryTransformer
468
                    .transform(queryType);
469

    
470
            DBQuery metacat = new DBQuery();
471

    
472
            boolean useXMLIndex = (new Boolean(PropertyService
473
                    .getProperty("database.usexmlindex"))).booleanValue();
474
            String xmlquery = "query"; // we don't care the query in resultset,
475
            // the query can be anything
476
            PrintWriter out = null; // we don't want metacat result, so set out null
477

    
478
            // parameter: queryspecification, user, group, usingIndexOrNot
479
            StringBuffer result = metacat.createResultDocument(xmlquery,
480
                    metacatQuery, out, username, groupNames, useXMLIndex);
481

    
482
            // create result set transfer		
483
            String saxparser = PropertyService.getProperty("xml.saxparser");
484
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
485
                    new StringReader(result.toString()), saxparser, queryType
486
                            .getNamespace().get_value());
487
            ResultsetType records = metacatResultsetParser.getEcogridResult();
488

    
489
            System.out
490
                    .println(EcogridResultsetTransformer.toXMLString(records));
491
            response.setContentType("text/xml");
492
            out = response.getWriter();
493
            out.print(EcogridResultsetTransformer.toXMLString(records));
494

    
495
        } catch (Exception e) {
496
            e.printStackTrace();
497
        }*/
498
        response.setContentType("text/xml");
499
        PrintWriter out = response.getWriter();
500
        out.print("<error>Query operation not yet supported by Metacat.</error>");
501
        out.close();
502
    }
503

    
504
    /**
505
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
506
     * 
507
     * @param objectId ID of data object to be inserted or updated
508
     * @throws IOException
509
     */
510
    private void putObject(String objectId) throws IOException {
511

    
512
        String action = request.getParameter(FUNCTION_KEYWORD);
513

    
514
        if (action.equals(FUNCTION_NAME_UPDATE)
515
                || action.equals(FUNCTION_NAME_INSERT)) {
516
            //
517
            // WARNING: This should not be a Reader if we are inserting data
518
            // Nor should it be read into a String
519
            // so this seems like a latent bug to me (MBJ; 16Mar2010)
520
            BufferedReader reader = request.getReader();
521
            StringBuffer buffer = new StringBuffer();
522
            String line = null;
523
            while ((line = reader.readLine()) != null) {
524
                buffer.append(line);
525
            }
526

    
527
            params.put("docid", new String[] { objectId });
528
            params.put("doctext", new String[] { buffer.toString().trim() });
529
            params.put("action", new String[] { action });
530

    
531
            if (username != null && !username.equals("public")) {
532
                PrintWriter out = response.getWriter();
533
                handler.handleInsertOrUpdateAction(request, response, out,
534
                        params, username, groupNames);
535
                out.close();
536
            } else {
537
                printError("Permission denied for user " + username, response);
538

    
539
            }
540
        } else {
541
            printError("Specifiy the operation type.(update or insert)",
542
                    response);
543
        }
544

    
545
    }
546

    
547
    /**
548
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction  
549
     * 
550
     * @param objectId ID of data object to be deleted
551
     * @throws IOException
552
     */
553
    private void deleteObject(String objectId) throws IOException {
554
        params.put("docid", new String[] { objectId });
555
        PrintWriter out = response.getWriter();
556
        handler.handleDeleteAction(out, params, request, response, username,
557
                groupNames);
558
        out.close();
559
    }
560

    
561
    /**
562
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
563
     * 
564
     * @throws IOException
565
     */
566
    private void login() throws IOException {
567
        PrintWriter out = response.getWriter();
568
        handler.handleLoginAction(out, params, request, response);
569
        out.close();
570
    }
571

    
572
    /**
573
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
574
     * 
575
     * @throws IOException
576
     */
577
    private void logout() throws IOException {
578
        PrintWriter out = response.getWriter();
579
        handler.handleLogoutAction(out, params, request, response);
580
        out.close();
581
    }
582

    
583
    /**
584
     * Prints xml response
585
     * @param message Message to be displayed
586
     * @param response Servlet response that xml message will be printed 
587
     * */
588
    private void printError(String message, HttpServletResponse response) {
589
        try {
590
            PrintWriter out = response.getWriter();
591
            response.setContentType("text/xml");
592
            out.println("<?xml version=\"1.0\"?>");
593
            out.println("<error>");
594
            out.println(message);
595
            out.println("</error>");
596
            out.close();
597
        } catch (IOException e) {
598
            e.printStackTrace();
599
        }
600
    }
601

    
602
}
(1-1/2)