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.*;
26
import java.util.*;
27

    
28
import javax.mail.BodyPart;
29
import javax.mail.MessagingException;
30
import javax.mail.internet.MimeMultipart;
31
import javax.servlet.ServletContext;
32
import javax.servlet.http.HttpServletRequest;
33
import javax.servlet.http.HttpServletResponse;
34
import java.text.DateFormat;
35
import java.text.ParsePosition;
36
import java.text.SimpleDateFormat;
37

    
38

    
39
import org.apache.commons.httpclient.util.DateParser;
40
import org.apache.commons.io.IOUtils;
41
import org.apache.log4j.Logger;
42
import org.dataone.service.exceptions.BaseException;
43
import org.dataone.service.exceptions.IdentifierNotUnique;
44
import org.dataone.service.exceptions.InsufficientResources;
45
import org.dataone.service.exceptions.InvalidRequest;
46
import org.dataone.service.exceptions.InvalidSystemMetadata;
47
import org.dataone.service.exceptions.InvalidToken;
48
import org.dataone.service.exceptions.NotAuthorized;
49
import org.dataone.service.exceptions.NotImplemented;
50
import org.dataone.service.exceptions.ServiceFailure;
51
import org.dataone.service.exceptions.UnsupportedType;
52
import org.dataone.service.exceptions.NotFound;
53
import org.dataone.service.types.*;
54
import org.jibx.runtime.BindingDirectory;
55
import org.jibx.runtime.IBindingFactory;
56
import org.jibx.runtime.IMarshallingContext;
57
import org.jibx.runtime.IUnmarshallingContext;
58
import org.jibx.runtime.JiBXException;
59

    
60
import edu.ucsb.nceas.metacat.DBUtil;
61
import edu.ucsb.nceas.metacat.IdentifierManager;
62
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
63
import edu.ucsb.nceas.metacat.MetacatHandler;
64
import edu.ucsb.nceas.metacat.dataone.CrudService;
65
import edu.ucsb.nceas.metacat.service.SessionService;
66
import edu.ucsb.nceas.metacat.util.RequestUtil;
67
import edu.ucsb.nceas.metacat.util.SessionData;
68

    
69
/**
70
 * 
71
 * Implements Earthgrid REST API to Metacat <br/><br/> 
72
 * 
73
 * <ul>
74
 * <li>
75
 * <h3> EarthGrid Query Service</h3>
76
 * <ul><li>
77
 * <h3>Get & Authenticated Get:</h3> 
78
 * is equal to Metacat's read action and returns a data file having
79
 * the specified <doc-id> in the resource path. For authenticated Get service, a session id must be provided 
80
 * in the query string. <br/><br/>
81
 * 
82
 * <b>REST URL:</b>	<code>GET, [context-root]/object/[doc-id]?sessionid=[sessionid] </code><br/>
83
 * <b>Returns:</b> data file <br/><br/>
84
 * </li>
85
 * 
86
 * <li>
87
 * <h3>Query & Authenticated Query:</h3> 
88
 * Metacat's equivalent is squery action but this function 
89
 * receives a Earthgrid query document and returns Earthgrid resultset document by transforming those documents
90
 * to Metacat's equivalents by the means of Metacat Implementation in Earthgrid library. For authenticated Query service 
91
 * a session id must be provided in the query string. See Earthgrid (a.k.a. Ecogrid) project for XSD files of 
92
 * query and resultset documents<br/><br/>
93
 * 
94
 * <b>REST URL:</b>	<code>POST, [context-root]/object?sessionid=[sessionid]</code>    <br/>
95
 * <b>POST Data:</b> Earthgrid query document , Content-type: <code>text/xml</code><br/>
96
 * <b>Returns:</b> Earthgrid resultset document<br/><br/>
97
 * 
98
 * </li>
99
 * </ul>
100
 * 
101
 * </li>
102
 * 
103
 * <li>
104
 * <h3> EarthGrid Authentication Service</h3>
105
 * <ul><li>
106
 * <h3>Login: </h3> 
107
 * Receives username and password parameters in POST data and 
108
 * returns SessionId (in XML format) or failure message and uses MetacatHandler's handleLoginAction function<br/><br/>
109
 *  
110
 * <b>REST URL:</b> <code>POST, [context-root]/session?op=login</code> <br/>
111
 * <b>POST Data:</b> username=[username]&password=[password], Content-type: <code>application/x-www-form-urlencoded</code>
112
 * <b>Returns:</b> sessionId in XML format<br/><br/>
113
 * </li>
114
 * 
115
 * <li>
116
 * <h3>Logout: </h3> 
117
 * Receives session Id parameters in querystring and returns xml message, calls 
118
 * MetacatHandler's handleLogoutAction function<br/><br/>
119
 *  
120
 * <b>REST URL:</b>	<code>GET, [context-root]/session?op=logout&sessionid=[sessionid]</code>   <br/>
121
 * <b>Returns:</b> message in XML format<br/><br/>
122
 * </li>
123
 * </ul>
124
 * 
125
 * <li>
126
 * <h3>EarthGrid Put Service</h3>
127
 * 
128
 * <ul>
129
 * <li><h3>Update/Insert: </h3>		
130
 * <br/>
131
 * <b>REST URL:</b>	<code>PUT, [context-root]/object/[doc-id]?op={update|insert}&sessionid=[sessionid]</code>   <br/>
132
 * <b>POST Data:</b> document object, Content-type: <code>text/xml</code><br/>
133
 * <b>Returns:</b> message in XML format<br/><br/>
134
 * </li>
135
 * 
136
 * <li><h3>Delete: </h3>		
137
 * <br/>
138
 * <b>REST URL:</b>	<code>DELETE, [context-root]/object/[doc-id]?sessionid=[sessionid]</code>   <br/>
139
 * <b>Returns:</b> message in XML format<br/><br/>
140
 * </li>
141

    
142
 * </ul>
143
 * </li>
144
 * 
145
 * <li>
146
 * <h3>EarthGrid Identifier Service</h3><br/>
147
 * 
148
 * <ul>
149
 * <li><h3>isRegistered: </h3>		<br/>
150
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=isregistered</code>   <br/>
151
 * <b>Returns:</b> message in XML format<br/><br/>
152
 * </li>
153

    
154
 * <li><h3>getAllDocIds:</h3>		<br/>		
155
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getalldocids</code>   <br/>
156
 * <b>Returns:</b> document id list in XML format<br/><br/>
157
 * </li>
158
 * 
159
 * <li><h3>addLSID Function:</h3> 
160
 * Metacat does not support this function 		<br/>
161
 * <b>REST URL:</b>	<code>PUT, [context-root]/identifier/[doc-id]</code>   <br/>
162
 * <b>Returns:</b> error message in XML format<br/><br/>
163
 * </li>
164
 * 
165
 * <li><h3>getNextRevision:</h3>		<br/>
166
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=getnextrevision</code>   <br/>
167
 * <b>Returns:</b> message in XML format<br/><br/>
168
 * </li>
169
 * 
170
 * <li><h3>getNextObject:</h3>		<br/>
171
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getnextobject&scope=[scope]</code>   <br/>
172
 * <b>Returns:</b> message in XML format<br/><br/>
173
 * </li>
174
 * 
175
 * </li>
176
 * </ul>
177
 * 
178
 */
179
public class ResourceHandler {
180

    
181
    /**HTTP Verb GET*/
182
    public static final byte GET = 1;
183
    /**HTTP Verb POST*/
184
    public static final byte POST = 2;
185
    /**HTTP Verb PUT*/
186
    public static final byte PUT = 3;
187
    /**HTTP Verb DELETE*/
188
    public static final byte DELETE = 4;
189

    
190
    /*
191
     * API Resources
192
     */
193
    private static final String RESOURCE_OBJECTS = "object";
194
    private static final String RESOURCE_META = "meta";
195
    private static final String RESOURCE_SESSION = "session";
196
    private static final String RESOURCE_IDENTIFIER = "identifier";
197
    private static final String RESOURCE_LOG = "log";
198

    
199
    /*
200
     * API Functions used as URL parameters
201
     */
202
    private static final String FUNCTION_KEYWORD = "op";
203
    private static final String FUNCTION_NAME_LOGIN = "login";
204
    private static final String FUNCTION_NAME_LOGOUT = "logout";
205
    private static final String FUNCTION_NAME_SET_ACCESS = "setaccess";
206
    private static final String FUNCTION_NAME_ISREGISTERED = "isregistered";
207
    private static final String FUNCTION_NAME_GETALLDOCS = "getalldocids";
208
    private static final String FUNCTION_NAME_GETNEXTREV = "getnextrevision";
209
    private static final String FUNCTION_NAME_GETNEXTOBJ = "getnextobject";
210
    private static final String FUNCTION_NAME_INSERT = "insert";
211
    private static final String FUNCTION_NAME_UPDATE = "update";
212
    private static final String FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA = "generatemissingsystemmetadata";
213

    
214
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
215
    
216
    private ServletContext servletContext;
217
    private Logger logMetacat;
218
    private MetacatHandler handler;
219
    private HttpServletRequest request;
220
    private HttpServletResponse response;
221
    private String username;
222
    private String password;
223
    private String sessionId;
224
    private String[] groupNames;
225

    
226
    private Hashtable<String, String[]> params;
227

    
228
    /**Initializes new instance by setting servlet context,request and response*/
229
    public ResourceHandler(ServletContext servletContext,
230
            HttpServletRequest request, HttpServletResponse response) {
231
        this.servletContext = servletContext;
232
        this.request = request;
233
        this.response = response;
234
    }
235

    
236
    /**
237
     * This function is called from REST APU servlet and handles each request to the servlet 
238
     * 
239
     * @param httpVerb (GET, POST, PUT or DELETE)
240
     */
241
    public void handle(byte httpVerb) {
242
        logMetacat = Logger.getLogger(ResourceHandler.class);
243
        try {
244
            String resource = request.getServletPath();
245
            String verb = "";
246
            
247
            System.out.println("handling verb " + httpVerb + " request with resource " + resource);
248
            boolean status = false;
249
            loadSessionData();
250

    
251
            if (resource != null) {
252
                resource = request.getServletPath().substring(1);
253

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

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

    
260
                if (resource.equals(RESOURCE_SESSION) && 
261
                    httpVerb == POST && 
262
                    params.get(FUNCTION_KEYWORD) != null) {
263
                    //System.out.println("function_keyword: " + params.get(FUNCTION_KEYWORD)[0]);
264
                        if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGIN)) {
265
                            login();
266
                            status = true;
267
                        } else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGOUT)) {
268
                            logout();
269
                            status = true;
270
                        } else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_SET_ACCESS)) {
271
                            setaccess();
272
                            status = true;
273
                            //System.out.println("done setting access");
274
                        }
275
                    } else if (resource.equals(RESOURCE_META)) {
276
                        if(params != null && params.get(FUNCTION_KEYWORD) != null &&
277
                           params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA))
278
                        { //generate system metadata for any object that is
279
                          //a) not system metadata itself
280
                          //b) does not already have a system metadata id in the systemmetadata table
281
                          //c) not a BIN object (data)
282
                            generateMissingSystemMetadata();
283
                            status = true;
284
                        }
285
                        else
286
                        {
287
                            String objectId = request.getPathInfo();
288
                            if (objectId != null && objectId.length() > 1) 
289
                            {
290
                                objectId = request.getPathInfo().substring(1);
291
                            }
292
                            getSystemMetadataObject(objectId);
293
                            status = true;
294
                        }
295
                            
296
                    } else if (resource.equals(RESOURCE_OBJECTS)) {
297
                    logMetacat.debug("D1 Rest: Starting resource processing...");
298
                    loadSessionData();
299

    
300
                    String objectId = request.getPathInfo();
301
                    if (objectId != null && objectId.length() > 1) 
302
                    {
303
                        objectId = request.getPathInfo().substring(1);
304
                    }
305
                    else
306
                    {
307
                        objectId = null;
308
                    }
309
                    
310
                    logMetacat.debug("verb:" + httpVerb);
311

    
312
                    if (httpVerb == GET) {
313
                        getObject(objectId);
314
                        status = true;
315
                    } else if (httpVerb == POST) {
316
                        putObject(objectId, FUNCTION_NAME_INSERT);
317
                        status = true;
318
                    } else if (httpVerb == PUT) {
319
                        putObject(objectId, FUNCTION_NAME_UPDATE);
320
                        status = true;
321
                    } else if (httpVerb == DELETE) {
322
                        deleteObject(objectId);
323
                        status = true;
324
                    }
325

    
326
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
327

    
328
                    String identifierId = request.getPathInfo();
329
                    if (identifierId != null && identifierId.length() > 1)
330
                        identifierId = request.getPathInfo().substring(1); //trim the slash
331

    
332
                    if (httpVerb == GET) {
333
                        String op = params.get(FUNCTION_KEYWORD)[0];
334
                        if (op.equals(FUNCTION_NAME_ISREGISTERED)) {
335
                            isRegistered(identifierId);
336
                            status = true;
337
                        } else if (op.equals(FUNCTION_NAME_GETALLDOCS)) {
338
                            getAllDocIds();
339
                            status = true;
340
                        } else if (op.equals(FUNCTION_NAME_GETNEXTREV)) {
341
                            getNextRevision(identifierId);
342
                            status = true;
343
                        } else if (op.equals(FUNCTION_NAME_GETNEXTOBJ)) {
344
                            getNextObject();
345
                            status = true;
346
                        } 
347

    
348
                    } else if (httpVerb == PUT) {
349
                        //Earthgrid API > Identifier Service > addLSID Function 
350
                        printError(
351
                                "This method is not supported by metacat.  To "
352
                                        + "add a new LSID, add a document to metacat.",
353
                                response);
354
                        status = true;
355
                    }
356

    
357
                } else if (resource.equals(RESOURCE_LOG)) {
358
                    //handle log events
359
                    if(httpVerb == GET)
360
                    {
361
                        getLog();
362
                        status = true;
363
                    }
364
                    else
365
                    {
366
                        printError("POST, PUT, DELETE is not supported for logs.", response);
367
                        status = true;
368
                    }
369
                    
370
                }
371
                
372
                if (!status)
373
                    printError("Incorrect parameters!", response);
374
            } else {
375
                printError("Incorrect resource!", response);
376
            }
377
        } catch (Exception e) {
378
            logMetacat.error(e.getMessage());
379
            e.printStackTrace();
380
        }
381
    }
382
    
383
    /**
384
     * get the logs from the CrudService based on passed params.  Available 
385
     * params are token, fromDate, toDate, event.  See 
386
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
387
     * for more info
388
     */
389
    private void getLog()
390
    {
391
        OutputStream out = null;
392
        try
393
        {
394
            out = response.getOutputStream();
395
            AuthToken token = new AuthToken(sessionId);
396
            String fromDateS = params.get("fromDate")[0];
397
            System.out.println("param fromDateS: " + fromDateS);
398
            Date fromDate = null;
399
            String toDateS = params.get("toDate")[0];
400
            System.out.println("param toDateS: " + toDateS);
401
            Date toDate = null;
402
            String eventS = params.get("event")[0];
403
            Event event = null;
404
            if(fromDateS != null)
405
            {
406
                //fromDate = CrudService.getInstance().parseDate(fromDateS);
407
                fromDate = dateFormat.parse(fromDateS);
408
            }
409
            if(toDateS != null)
410
            {
411
                //toDate = CrudService.getInstance().parseDate(toDateS);
412
                toDate = dateFormat.parse(toDateS);
413
            }
414
            if(eventS != null)
415
            {
416
                event = Event.convert(eventS);
417
            }
418
            System.out.println("fromDate: " + fromDate + " toDate: " + toDate);
419
            
420
            System.out.println("calling crudservice.getLogRecords");
421
            Log log = CrudService.getInstance().getLogRecords(token, fromDate, toDate, event);
422
            serializeServiceType(Log.class, log, out);
423
        }
424
        catch(Exception e)
425
        {
426
            String msg = "Could not get logs from CrudService: " + e.getMessage();
427
            ServiceFailure sf = new ServiceFailure("1490", msg);
428
            logMetacat.error(msg);
429
            e.printStackTrace();
430
            serializeException(sf, out);
431
        }
432
    }
433
    
434
    /**
435
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions  
436
     */
437
    private void initParams() {
438

    
439
        String name = null;
440
        String[] value = null;
441
        Enumeration paramlist = request.getParameterNames();
442
        while (paramlist.hasMoreElements()) {
443
            name = (String) paramlist.nextElement();
444
            value = request.getParameterValues(name);
445
            params.put(name, value);
446
        }
447
    }
448

    
449
    /**
450
     * 
451
     * Load user details of metacat session from the request 
452
     * 
453
     */
454
    private void loadSessionData()
455
      throws Exception
456
    {
457
        SessionData sessionData = RequestUtil.getSessionData(request);
458
        try
459
        {
460
            username = null;
461
            password = null;
462
            groupNames = null;
463
            sessionId = null;
464
            
465
            boolean validSession = false;
466
            SessionService ss = SessionService.getInstance();
467
            System.out.println("sessionData: " + sessionData);
468
            if(sessionData == null)
469
            {
470
                username = "public";
471
                sessionId = "0";
472
                System.out.println("sessiondata is null.  Creating a public session.");
473
                return;
474
            }
475
            
476
            System.out.println("username: " + sessionData.getUserName());
477
            System.out.println("sessionid: " + sessionData.getId());
478
            //validate the session
479
            if(ss.isSessionRegistered(sessionData.getId()) && 
480
               !(sessionData.getUserName().equals("public") || sessionData.getId().equals("0")))
481
            {
482
                validSession = true;
483
            }
484
            
485
            if(validSession)
486
            {
487
                //if the session is valid, set these variables
488
                username = sessionData.getUserName();
489
                password = sessionData.getPassword();
490
                groupNames = sessionData.getGroupNames();
491
                sessionId = sessionData.getId();
492
                System.out.println("setting sessionid to " + sessionId);
493
                System.out.println("username: " + username);
494
            }
495
            
496
            //if the session is not valid or the username is null, set
497
            //username to public
498
            if (username == null) 
499
            {
500
                System.out.println("setting username to public.");
501
                username = "public";
502
            }
503
        }
504
        catch(Exception e)
505
        {
506
            e.printStackTrace();
507
            throw new Exception("Could not load the session data: " + e.getMessage());
508
        }
509
    }
510
    
511
    /**
512
     * generate missing system metadata for any science metadata objects
513
     * that don't already have it. https://trac.dataone.org/ticket/591
514
     * 
515
     * called with POST meta/?op=generatemissingsystemmetadata
516
     */
517
    private void generateMissingSystemMetadata()
518
    {
519
        AuthToken token = new AuthToken(sessionId);
520
        CrudService.getInstance().generateMissingSystemMetadata(token);
521
    }
522

    
523
    /**
524
     *  Earthgrid API > Identifier Service > isRegistered Function : 
525
     *  calls MetacatHandler > handleIdIsRegisteredAction
526
     * @param guid
527
     * @throws IOException
528
     */
529
    private void isRegistered(String guid) throws IOException
530
    {
531
        
532
        // Look up the localId for this guid
533
        IdentifierManager im = IdentifierManager.getInstance();
534
        String localId = "";
535
        try {
536
            localId = im.getLocalId(guid);
537
        } catch (McdbDocNotFoundException e) {
538
            // TODO: Need to return the proper DataONE exception
539
        }
540
        
541
        params.put("docid", new String[] { localId });
542
        PrintWriter out = response.getWriter();
543
        handler.handleIdIsRegisteredAction(out, params, response);
544
        out.close();
545
    }
546

    
547
    /**
548
     * Earthgrid API > Identifier Service > getAllDocIds Function : 
549
     * calls MetacatHandler > handleGetAllDocidsAction
550
     * @throws IOException
551
     */
552
    private void getAllDocIds() throws IOException {
553
        PrintWriter out = response.getWriter();
554
        handler.handleGetAllDocidsAction(out, params, response);
555
        out.close();
556
    }
557

    
558
    /**
559
     * Earthgrid API > Identifier Service > getNextRevision Function : 
560
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
561
     * @param guid
562
     * @throws IOException
563
     */
564
    private void getNextRevision(String guid) throws IOException 
565
    {
566
        params.put("docid", new String[] { guid });
567
        PrintWriter out = response.getWriter();
568
        //handler.handleGetRevisionAndDocTypeAction(out, params);
569

    
570
        try {
571
            // Make sure there is a docid
572
            if (guid == null || guid.equals("")) {
573
                throw new Exception("User didn't specify docid!");
574
            }
575

    
576
            // Look up the localId for this guid
577
            IdentifierManager im = IdentifierManager.getInstance();
578
            String localId = "";
579
            try {
580
                localId = im.getLocalId(guid);
581
            } catch (McdbDocNotFoundException e) {
582
                // TODO: Need to return the proper DataONE exception
583
            }
584
           
585
            // Create a DBUtil object
586
            DBUtil dbutil = new DBUtil();
587
            // Get a rev and doctype
588
            String revAndDocType = dbutil
589
                    .getCurrentRevisionAndDocTypeForGivenDocument(localId);
590
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
591

    
592
            out.println("<?xml version=\"1.0\"?>");
593
            out.print("<next-revision>");
594
            out.print(revision);
595
            out.print("</next-revision>");
596

    
597
        } catch (Exception e) {
598
            // Handle exception
599
            out.println("<?xml version=\"1.0\"?>");
600
            out.println("<error>");
601
            out.println(e.getMessage());
602
            out.println("</error>");
603
        }
604

    
605
        out.close();
606
    }
607

    
608
    /**
609
     * Earthgrid API > Identifier Service > getNextObject Function : 
610
     * calls MetacatHandler > handleGetMaxDocidAction
611
     * @throws IOException
612
     */
613
    private void getNextObject() throws IOException {
614
        PrintWriter out = response.getWriter();
615
        handler.handleGetMaxDocidAction(out, params, response);
616
        out.close();
617
    }
618

    
619
    /**
620
     * Implements REST version of DataONE CRUD API --> get
621
     * @param guid ID of data object to be read
622
     */
623
    private void getObject(String guid) {
624
        CrudService cs = CrudService.getInstance();
625
        cs.setParamsFromRequest(request);
626
        AuthToken token = new AuthToken(sessionId);
627
        OutputStream out = null;
628
        try {
629
            out = response.getOutputStream();
630
            if(guid != null)
631
            { //get a specific document
632
                Identifier id = new Identifier();
633
                id.setValue(guid);
634
                InputStream data = cs.get(token, id);
635
                IOUtils.copyLarge(data, response.getOutputStream());
636
            }
637
            else
638
            { //call listObjects with specified params
639
                Date startTime = null;
640
                Date endTime = null;
641
                ObjectFormat objectFormat = null;
642
                boolean replicaStatus = false;
643
                int start = 0;
644
                int count = 1000;
645
                Enumeration paramlist = request.getParameterNames();
646
                while (paramlist.hasMoreElements()) 
647
                { //parse the params and make the crud call
648
                    String name = (String) paramlist.nextElement();
649
                    String[] value = (String[])request.getParameterValues(name);
650
                    /*for(int i=0; i<value.length; i++)
651
                    {
652
                        System.out.println("name: " + name + " value: " + value[i]);
653
                    }*/
654
                    if(name.equals("startTime") && value != null)
655
                    {
656
                        try
657
                        {
658
                          startTime = dateFormat.parse(value[0]);
659
                        }
660
                        catch(Exception e)
661
                        {  //if we can't parse it, just don't use the startTime param
662
                            System.out.println("Could not parse startTime: " + value[0]);
663
                            startTime = null;
664
                        }
665
                    }
666
                    else if(name.equals("endTime") && value != null)
667
                    {
668
                        try
669
                        {
670
                          endTime = dateFormat.parse(value[0]);
671
                        }
672
                        catch(Exception e)
673
                        {  //if we can't parse it, just don't use the endTime param
674
                            System.out.println("Could not parse endTime: " + value[0]);
675
                            endTime = null;
676
                        }
677
                    }
678
                    else if(name.equals("objectFormat") && value != null)
679
                    {
680
                        objectFormat = ObjectFormat.convert(value[0]);
681
                    }
682
                    else if(name.equals("replicaStatus") && value != null)
683
                    {
684
                        if(value != null && 
685
                           value.length > 0 && 
686
                           (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
687
                        {
688
                            replicaStatus = true;
689
                        }
690
                    }
691
                    else if(name.equals("start") && value != null)
692
                    {
693
                        start = new Integer(value[0]).intValue();
694
                    }
695
                    else if(name.equals("count") && value != null)
696
                    {
697
                        count = new Integer(value[0]).intValue();
698
                    }
699
                }
700
                //make the crud call
701
                /*System.out.println("token: " + token + " startTime: " + startTime +
702
                        " endtime: " + endTime + " objectFormat: " + 
703
                        objectFormat + " replicaStatus: " + replicaStatus + 
704
                        " start: " + start + " count: " + count);
705
                */
706
                ObjectList ol = cs.listObjects(token, startTime, endTime, 
707
                        objectFormat, replicaStatus, start, count);
708
                ol = cs.listObjects(token, startTime, endTime, objectFormat, replicaStatus, start, count);
709
                
710
                StringReader sr = new StringReader(ol.toString());                
711
                out = response.getOutputStream();                
712
                // Serialize and write it to the output stream
713
                
714
                try {
715
                    serializeServiceType(ObjectList.class, ol, out);
716
                } catch (JiBXException e) {
717
                    throw new ServiceFailure("1190", "Failed to serialize ObjectList: " + e.getMessage());
718
                }
719
            }
720
        } catch (BaseException e) {
721
                serializeException(e, out);
722
        } catch (IOException e) {
723
            e.printStackTrace();
724
            ServiceFailure sf = new ServiceFailure("1030", 
725
                    "IO Error in ResourceHandler.getObject: " + e.getMessage());
726
            serializeException(sf, out); 
727
        } catch(NumberFormatException ne) {
728
            InvalidRequest ir = new InvalidRequest("1030", "Invalid format for parameter: " + ne.getMessage());
729
            serializeException(ir, out);
730
        } catch (Exception e) {
731
            e.printStackTrace();
732
            ServiceFailure sf = new ServiceFailure("1030", 
733
                    "Exception " + e.getClass().getName() + " raised while handling listObjects request: " + 
734
                    e.getMessage());
735
            serializeException(sf, out);
736
        }
737
    }
738

    
739
    /**
740
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
741
     * @param guid ID of data object to be read
742
     */
743
    private void getSystemMetadataObject(String guid) {
744
        CrudService cs = CrudService.getInstance();
745
        cs.setParamsFromRequest(request);
746
        AuthToken token = new AuthToken(sessionId);
747
        OutputStream out = null;
748
        try {
749
            out = response.getOutputStream();
750
            Identifier id = new Identifier();
751
            id.setValue(guid);
752
            SystemMetadata sysmeta = cs.getSystemMetadata(token, id);
753
            
754
            // Serialize and write it to the output stream
755
            try {
756
                serializeServiceType(SystemMetadata.class, sysmeta, out);
757
            } catch (JiBXException e) {
758
                throw new ServiceFailure("1190", "Failed to serialize SystemMetadata: " + e.getMessage());
759
            }
760
        } catch (BaseException e) {
761
                serializeException(e, out);
762
        } catch (IOException e) {
763
            ServiceFailure sf = new ServiceFailure("1030", 
764
                    "Error in ResourceHandler.getSystemMetadataObject: " + e.getMessage());
765
            serializeException(sf, out);
766
        } finally {
767
            IOUtils.closeQuietly(out);
768
        }
769
    }
770
    
771
    /**
772
     * serialize an object of type to out
773
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
774
     * @param object the object to serialize
775
     * @param out the stream to serialize it to
776
     * @throws JiBXException
777
     */
778
    private void serializeServiceType(Class type, Object object, OutputStream out)
779
      throws JiBXException
780
    {
781
        IBindingFactory bfact = BindingDirectory.getFactory(type);
782
        IMarshallingContext mctx = bfact.createMarshallingContext();
783
        mctx.marshalDocument(object, "UTF-8", null, out);
784
    }
785
    
786
    /**
787
     * deserialize an object of type from is
788
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
789
     * @param is the stream to deserialize from
790
     * @throws JiBXException
791
     */
792
    private Object deserializeServiceType(Class type, InputStream is)
793
      throws JiBXException
794
    {
795
        IBindingFactory bfact = BindingDirectory.getFactory(type);
796
        IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
797
        Object o = (Object) uctx.unmarshalDocument(is, null);
798
        return o;
799
    }
800
    
801
    /**
802
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query 
803
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
804
     * 
805
     * NOTE:
806
     *      This is the only method that uses EcoGrid classes for its implementation.  
807
     *      It does so because it takes an EcoGrid Query as input, and outputs an
808
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
809
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
810
     *      This creates a circular dependency, because the Metacat classes are needed
811
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
812
     *      method.  This circularity could be resolved by moving the EcoGrid classes
813
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
814
     *      favor of these REST interfaces, this circular dependency can be eliminated.
815
     *        
816
     * @throws Exception
817
     */
818
    private void query() throws Exception {
819
        /*  This block commented out because of the EcoGrid circular dependency.
820
         *  For now, query will not be supported until the circularity can be
821
         *  resolved, probably by moving the ecogrid query syntax transformers
822
         *  directly into the Metacat codebase.  MBJ 2010-02-03
823
         
824
        try {
825
            EcogridQueryParser parser = new EcogridQueryParser(request
826
                    .getReader());
827
            parser.parseXML();
828
            QueryType queryType = parser.getEcogridQuery();
829
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
830
                new EcogridJavaToMetacatJavaQueryTransformer();
831
            QuerySpecification metacatQuery = queryTransformer
832
                    .transform(queryType);
833

    
834
            DBQuery metacat = new DBQuery();
835

    
836
            boolean useXMLIndex = (new Boolean(PropertyService
837
                    .getProperty("database.usexmlindex"))).booleanValue();
838
            String xmlquery = "query"; // we don't care the query in resultset,
839
            // the query can be anything
840
            PrintWriter out = null; // we don't want metacat result, so set out null
841

    
842
            // parameter: queryspecification, user, group, usingIndexOrNot
843
            StringBuffer result = metacat.createResultDocument(xmlquery,
844
                    metacatQuery, out, username, groupNames, useXMLIndex);
845

    
846
            // create result set transfer		
847
            String saxparser = PropertyService.getProperty("xml.saxparser");
848
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
849
                    new StringReader(result.toString()), saxparser, queryType
850
                            .getNamespace().get_value());
851
            ResultsetType records = metacatResultsetParser.getEcogridResult();
852

    
853
            System.out
854
                    .println(EcogridResultsetTransformer.toXMLString(records));
855
            response.setContentType("text/xml");
856
            out = response.getWriter();
857
            out.print(EcogridResultsetTransformer.toXMLString(records));
858

    
859
        } catch (Exception e) {
860
            e.printStackTrace();
861
        }*/
862
        response.setContentType("text/xml");
863
        PrintWriter out = response.getWriter();
864
        out.print("<error>Query operation not yet supported by Metacat.</error>");
865
        out.close();
866
    }
867
    
868
    private String streamToString(InputStream is)
869
    throws IOException
870
    {
871
        byte b[] = new byte[1024];
872
        int numread = is.read(b, 0, 1024);
873
        String response = new String();
874
        while(numread != -1)
875
        {
876
            response += new String(b, 0, numread);
877
            numread = is.read(b, 0, 1024);
878
        }
879
        return response;
880
    }
881

    
882
    private InputStream stringToStream(String s)
883
    throws IOException
884
    {
885
        ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
886
        return bais;
887
    }
888
    
889
    
890
    /**
891
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
892
     * 
893
     * @param guid ID of data object to be inserted or updated
894
     * @throws IOException
895
     */
896
    private void putObject(String guid, String action) {
897
        logMetacat.debug("Entering putObject: " + guid + "/" + action);
898
        OutputStream out = null;
899
        try {
900
            out = response.getOutputStream();
901
        } catch (IOException e1) {
902
            logMetacat.error("Could not get the output stream for writing in putObject");
903
        }
904
        try {
905
            
906
            // Read the incoming data from its Mime Multipart encoding
907
            logMetacat.debug("Disassembling MIME multipart form");
908
            InputStream object = null;
909
            InputStream sysmeta = null;
910
            
911
            //HACK: Since mmp seems to have a bug where large object parts get truncated,
912
            //parse the stream here.  This has the disavantage of putting the
913
            //stream into memory.
914
            String s = streamToString(request.getInputStream());
915
            String searchString = "Content-Disposition: attachment; filename=systemmetadata";
916
            String endString = "------=_Part";
917
            int searchStringIndex = s.indexOf(searchString);
918
            sysmeta = new ByteArrayInputStream(
919
                    s.substring(searchStringIndex +
920
                            searchString.length(), 
921
                            s.indexOf(endString, searchStringIndex)).trim().getBytes());
922
            
923
            searchString = "Content-Disposition: attachment; filename=object";
924
            searchStringIndex = s.indexOf(searchString);
925
            object = new ByteArrayInputStream(
926
                    s.substring(searchStringIndex +
927
                            searchString.length(), 
928
                            s.indexOf(endString, searchStringIndex)).trim().getBytes());
929
            
930
            /*
931
            //This code should work, however there seems to be a bug in part.getInputStream()
932
            //where it does not return the entire stream.  Only about the first 6kB 
933
            //are actually returned.
934
            MimeMultipart mmp = new MimeMultipart(new InputStreamDataSource("message", request.getInputStream()));
935
            logMetacat.debug("MMP created.");
936
            
937
            //mmp.writeTo(System.out);
938
            for (int i = 0; i < mmp.getCount(); i++) {
939
                BodyPart part = mmp.getBodyPart(i);
940
                String name = part.getFileName();
941
                logMetacat.debug("Part name is: " + name);
942
                logMetacat.debug("Part has class name: " + part.getClass().getName());
943
                
944
                if (name.equals("object")) {
945
                    object = part.getInputStream();
946
                    try
947
                    {
948
                        String s = streamToString(object);
949
                        System.out.println("doc text: " + s);
950
                        System.out.println("size of doc text is: " + s.getBytes().length);
951
                        object = stringToStream(s);
952
                    }
953
                    catch(Exception e)
954
                    {}
955
                    System.out.println("Found object part, size is: " + part.getSize());
956
                } else if (name.equals("systemmetadata")) {
957
                    sysmeta = part.getInputStream();
958
                    try
959
                    {
960
                      String s = streamToString(sysmeta);
961
                      System.out.println("system metadata part: " + s);
962
                      sysmeta = stringToStream(s);
963
                    }
964
                    catch(Exception e){}
965
                    System.out.println("system metadata part, size is " + part.getSize());
966
                    logMetacat.debug("Found sysmeta part, size is: " + part.getSize());
967
                } else {
968
                    throw new InvalidRequest("1000", "Request had malformed MIME part with name: " + name);
969
                }
970
            }*/
971
            
972
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
973

    
974
                // Check if the objectId exists
975
                IdentifierManager im = IdentifierManager.getInstance();
976
                if (im.identifierExists(guid)) {
977
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
978
                }
979

    
980
                logMetacat.debug("Commence creation...");
981
                IBindingFactory bfact =
982
                    BindingDirectory.getFactory(SystemMetadata.class);
983
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
984
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
985

    
986
                CrudService cs = CrudService.getInstance();
987
                AuthToken token = new AuthToken(sessionId); 
988
                cs.setParamsFromRequest(request);
989
                Identifier id = new Identifier();
990
                id.setValue(guid);
991
                cs.create(token, id, object, m);
992
                
993
            } else if (action.equals(FUNCTION_NAME_UPDATE)) { //handle updates
994
                IdentifierManager im = IdentifierManager.getInstance();
995
                CrudService cs = CrudService.getInstance();
996
                Identifier obsoletedGuid = new Identifier();
997
                Identifier id = new Identifier();
998
                id.setValue(guid);
999
                AuthToken token = new AuthToken(sessionId);
1000
                
1001
                //do some checks
1002
                if(params.get("obsoletedGuid") == null)
1003
                {
1004
                    throw new InvalidRequest("1202", "obsoletedGuid must be contained in the request parameters.");
1005
                }
1006
                //get the obsoletedGuid
1007
                String[] obsGuidS = params.get("obsoletedGuid");
1008
                obsoletedGuid.setValue(obsGuidS[0]);
1009
                
1010
                if (!im.identifierExists(obsoletedGuid.getValue())) 
1011
                {
1012
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1013
                }
1014
                
1015
                
1016
                logMetacat.debug("Commence update...");
1017
                
1018
                //get the systemmetadata
1019
                IBindingFactory bfact =
1020
                        BindingDirectory.getFactory(SystemMetadata.class);
1021
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1022
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1023
                
1024
                //do the update
1025
                try
1026
                {
1027
                    cs.setParamsFromRequest(request);
1028
                    Identifier rId = cs.update(token, id, object, obsoletedGuid, m);
1029
                }
1030
                catch(NotFound e)
1031
                {
1032
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1033
                }
1034
                
1035
            } else {
1036
                throw new InvalidRequest("1000", "Operation must be create or update.");
1037
            }
1038
        } catch (NotAuthorized e) {
1039
            serializeException(e, out);
1040
        } catch (InvalidToken e) {
1041
            serializeException(e, out);
1042
        } catch (ServiceFailure e) {
1043
            serializeException(e, out);
1044
        } catch (IdentifierNotUnique e) {
1045
            serializeException(e, out);
1046
        } catch (UnsupportedType e) {
1047
            serializeException(e, out);
1048
        } catch (InsufficientResources e) {
1049
            serializeException(e, out);
1050
        } catch (InvalidSystemMetadata e) {
1051
            serializeException(e, out);
1052
        } catch (NotImplemented e) {
1053
            serializeException(e, out);
1054
        } catch (InvalidRequest e) {
1055
            serializeException(e, out);
1056
        } /*catch (MessagingException e) {
1057
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1058
            serializeException(sf, out);
1059
        }*/ catch (IOException e) {
1060
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1061
            serializeException(sf, out);
1062
        } catch (JiBXException e) {
1063
            e.printStackTrace(System.out);
1064
            InvalidSystemMetadata ism = new InvalidSystemMetadata("1080", e.getMessage());
1065
            serializeException(ism, out);
1066
        }
1067
    }
1068

    
1069
    /**
1070
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction  
1071
     * 
1072
     * @param guid ID of data object to be deleted
1073
     * @throws IOException
1074
     */
1075
    private void deleteObject(String guid) throws IOException 
1076
    {
1077
        // Look up the localId for this global identifier
1078
        IdentifierManager im = IdentifierManager.getInstance();
1079
        String localId = "";
1080
        try {
1081
            localId = im.getLocalId(guid);
1082
        } catch (McdbDocNotFoundException e) {
1083
            // TODO: Need to return the proper DataONE exception
1084
        }
1085
        
1086
        params.put("docid", new String[] { localId });
1087
        PrintWriter out = response.getWriter();
1088
        handler.handleDeleteAction(out, params, request, response, username,
1089
                groupNames);
1090
        out.close();
1091
    }
1092
    
1093
    /**
1094
     * set the access perms on a document
1095
     * @throws IOException
1096
     */
1097
    private void setaccess() throws Exception
1098
    {
1099
        try
1100
        {
1101
            String guid = params.get("guid")[0];
1102
            Identifier id = new Identifier();
1103
            id.setValue(guid);
1104
            AuthToken token = new AuthToken(sessionId);
1105
            String principal = params.get("principal")[0];
1106
            String permission = params.get("permission")[0];
1107
            String permissionType = params.get("permissionType")[0];
1108
            String permissionOrder = params.get("permissionOrder")[0];
1109
            String setSystemMetadata = params.get("setsystemmetadata")[0];
1110
            boolean ssm = false;
1111
            if(setSystemMetadata.equals("true") || setSystemMetadata.equals("TRUE") ||
1112
                    setSystemMetadata.equals("yes"))
1113
            {
1114
                ssm = true;
1115
            }
1116
            CrudService cs = CrudService.getInstance();
1117
            cs.setAccess(token, id, principal, permission, permissionType, permissionOrder, ssm);
1118
        }
1119
        catch(Exception e)
1120
        {
1121
            printError("Error setting access in ResourceHandler: " + e.getMessage(), response);
1122
            throw e;
1123
        }
1124
    }
1125

    
1126
    /**
1127
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
1128
     * 
1129
     * @throws IOException
1130
     */
1131
    private void login() throws IOException {
1132
        PrintWriter out = response.getWriter();
1133
        handler.handleLoginAction(out, params, request, response);
1134
        out.close();
1135
    }
1136

    
1137
    /**
1138
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
1139
     * 
1140
     * @throws IOException
1141
     */
1142
    private void logout() throws IOException {
1143
        PrintWriter out = response.getWriter();
1144
        handler.handleLogoutAction(out, params, request, response);
1145
        out.close();
1146
    }
1147

    
1148
    /**
1149
     * Prints xml response
1150
     * @param message Message to be displayed
1151
     * @param response Servlet response that xml message will be printed 
1152
     * */
1153
    private void printError(String message, HttpServletResponse response) {
1154
        try {
1155
            PrintWriter out = response.getWriter();
1156
            response.setContentType("text/xml");
1157
            out.println("<?xml version=\"1.0\"?>");
1158
            out.println("<error>");
1159
            out.println(message);
1160
            out.println("</error>");
1161
            out.close();
1162
        } catch (IOException e) {
1163
            e.printStackTrace();
1164
        }
1165
    }
1166
    
1167
    private void serializeException(BaseException e, OutputStream out) {
1168
        // TODO: Use content negotiation to determine which return format to use
1169
        response.setContentType("text/xml");
1170
        response.setStatus(e.getCode());
1171
        try {
1172
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
1173
        } catch (IOException e1) {
1174
            logMetacat.error("Error writing exception to stream. " 
1175
                    + e1.getMessage());
1176
        }
1177
    }
1178

    
1179
}
(2-2/3)