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.MetacatHandler;
40
import edu.ucsb.nceas.metacat.util.RequestUtil;
41
import edu.ucsb.nceas.metacat.util.SessionData;
42

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

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

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

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

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

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

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

    
194
    private Hashtable<String, String[]> params;
195

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

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

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

    
218
    }
219

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

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

    
233
        if (username == null)
234
            username = "public";
235
    }
236

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

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

    
248
            boolean status = false;
249

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

    
254
                params = new Hashtable<String, String[]>();
255
                initParams();
256

    
257
                Timer timer = new Timer();
258
                handler = new MetacatHandler(servletContext, timer);
259

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

    
273
                    loadSessionData();
274

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

    
279
                    System.out.println("verb:" + httpVerb);
280

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

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

    
297
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
298

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

    
303
                    System.out.println("identifierId:" + identifierId);
304

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

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

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

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

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

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

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

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

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

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

    
401
        out.close();
402
    }
403

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

    
414
    /**
415
     * Earthgrid API > Query Service > Get Function : calls MetacatHandler > handleReadAction
416
     * 
417
     * @param objectId ID of data object to be read
418
     */
419
    private void getObject(String objectId) {
420
        params.put("docid", new String[] { objectId });
421
        handler.handleReadAction(params, request, response, username, password,
422
                groupNames);
423
    }
424

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

    
458
            DBQuery metacat = new DBQuery();
459

    
460
            boolean useXMLIndex = (new Boolean(PropertyService
461
                    .getProperty("database.usexmlindex"))).booleanValue();
462
            String xmlquery = "query"; // we don't care the query in resultset,
463
            // the query can be anything
464
            PrintWriter out = null; // we don't want metacat result, so set out null
465

    
466
            // parameter: queryspecification, user, group, usingIndexOrNot
467
            StringBuffer result = metacat.createResultDocument(xmlquery,
468
                    metacatQuery, out, username, groupNames, useXMLIndex);
469

    
470
            // create result set transfer		
471
            String saxparser = PropertyService.getProperty("xml.saxparser");
472
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
473
                    new StringReader(result.toString()), saxparser, queryType
474
                            .getNamespace().get_value());
475
            ResultsetType records = metacatResultsetParser.getEcogridResult();
476

    
477
            System.out
478
                    .println(EcogridResultsetTransformer.toXMLString(records));
479
            response.setContentType("text/xml");
480
            out = response.getWriter();
481
            out.print(EcogridResultsetTransformer.toXMLString(records));
482

    
483
        } catch (Exception e) {
484
            e.printStackTrace();
485
        }*/
486
        response.setContentType("text/xml");
487
        PrintWriter out = response.getWriter();
488
        out.print("<error>Query operation not yet supported by Metacat.</error>");
489
        out.close();
490
    }
491

    
492
    /**
493
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
494
     * 
495
     * @param objectId ID of data object to be inserted or updated
496
     * @throws IOException
497
     */
498
    private void putObject(String objectId) throws IOException {
499

    
500
        String action = request.getParameter(FUNCTION_KEYWORD);
501

    
502
        if (action.equals(FUNCTION_NAME_UPDATE)
503
                || action.equals(FUNCTION_NAME_INSERT)) {
504
            BufferedReader reader = request.getReader();
505
            StringBuffer buffer = new StringBuffer();
506
            String line = null;
507
            while ((line = reader.readLine()) != null) {
508
                buffer.append(line);
509
            }
510

    
511
            params.put("docid", new String[] { objectId });
512
            params.put("doctext", new String[] { buffer.toString().trim() });
513
            params.put("action", new String[] { action });
514

    
515
            if (username != null && !username.equals("public")) {
516
                PrintWriter out = response.getWriter();
517
                handler.handleInsertOrUpdateAction(request, response, out,
518
                        params, username, groupNames);
519
                out.close();
520
            } else {
521
                printError("Permission denied for user " + username, response);
522

    
523
            }
524
        } else {
525
            printError("Specifiy the operation type.(update or insert)",
526
                    response);
527
        }
528

    
529
    }
530

    
531
    /**
532
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction  
533
     * 
534
     * @param objectId ID of data object to be deleted
535
     * @throws IOException
536
     */
537
    private void deleteObject(String objectId) throws IOException {
538
        params.put("docid", new String[] { objectId });
539
        PrintWriter out = response.getWriter();
540
        handler.handleDeleteAction(out, params, request, response, username,
541
                groupNames);
542
        out.close();
543
    }
544

    
545
    /**
546
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
547
     * 
548
     * @throws IOException
549
     */
550
    private void login() throws IOException {
551
        PrintWriter out = response.getWriter();
552
        handler.handleLoginAction(out, params, request, response);
553
        out.close();
554
    }
555

    
556
    /**
557
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
558
     * 
559
     * @throws IOException
560
     */
561
    private void logout() throws IOException {
562
        PrintWriter out = response.getWriter();
563
        handler.handleLogoutAction(out, params, request, response);
564
        out.close();
565
    }
566

    
567
    /**
568
     * Prints xml response
569
     * @param message Message to be displayed
570
     * @param response Servlet response that xml message will be printed 
571
     * */
572
    private void printError(String message, HttpServletResponse response) {
573
        try {
574
            PrintWriter out = response.getWriter();
575
            response.setContentType("text/xml");
576
            out.println("<?xml version=\"1.0\"?>");
577
            out.println("<error>");
578
            out.println(message);
579
            out.println("</error>");
580
            out.close();
581
        } catch (IOException e) {
582
            e.printStackTrace();
583
        }
584
    }
585

    
586
}
(1-1/2)