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'Z'");
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

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

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

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

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

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

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

    
328
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
329

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

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

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

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

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

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

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

    
541
    /**
542
     * Earthgrid API > Identifier Service > getAllDocIds Function : 
543
     * calls MetacatHandler > handleGetAllDocidsAction
544
     * @throws IOException
545
     */
546
    private void getAllDocIds() throws IOException {
547
        PrintWriter out = response.getWriter();
548
        handler.handleGetAllDocidsAction(out, params, response);
549
        out.close();
550
    }
551

    
552
    /**
553
     * Earthgrid API > Identifier Service > getNextRevision Function : 
554
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
555
     * @param guid
556
     * @throws IOException
557
     */
558
    private void getNextRevision(String guid) throws IOException 
559
    {
560
        params.put("docid", new String[] { guid });
561
        PrintWriter out = response.getWriter();
562
        //handler.handleGetRevisionAndDocTypeAction(out, params);
563

    
564
        try {
565
            // Make sure there is a docid
566
            if (guid == null || guid.equals("")) {
567
                throw new Exception("User didn't specify docid!");
568
            }
569

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

    
586
            out.println("<?xml version=\"1.0\"?>");
587
            out.print("<next-revision>");
588
            out.print(revision);
589
            out.print("</next-revision>");
590

    
591
        } catch (Exception e) {
592
            // Handle exception
593
            out.println("<?xml version=\"1.0\"?>");
594
            out.println("<error>");
595
            out.println(e.getMessage());
596
            out.println("</error>");
597
        }
598

    
599
        out.close();
600
    }
601

    
602
    /**
603
     * Earthgrid API > Identifier Service > getNextObject Function : 
604
     * calls MetacatHandler > handleGetMaxDocidAction
605
     * @throws IOException
606
     */
607
    private void getNextObject() throws IOException {
608
        PrintWriter out = response.getWriter();
609
        handler.handleGetMaxDocidAction(out, params, response);
610
        out.close();
611
    }
612

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

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

    
829
            DBQuery metacat = new DBQuery();
830

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

    
837
            // parameter: queryspecification, user, group, usingIndexOrNot
838
            StringBuffer result = metacat.createResultDocument(xmlquery,
839
                    metacatQuery, out, username, groupNames, useXMLIndex);
840

    
841
            // create result set transfer		
842
            String saxparser = PropertyService.getProperty("xml.saxparser");
843
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
844
                    new StringReader(result.toString()), saxparser, queryType
845
                            .getNamespace().get_value());
846
            ResultsetType records = metacatResultsetParser.getEcogridResult();
847

    
848
            System.out
849
                    .println(EcogridResultsetTransformer.toXMLString(records));
850
            response.setContentType("text/xml");
851
            out = response.getWriter();
852
            out.print(EcogridResultsetTransformer.toXMLString(records));
853

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

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

    
969
                // Check if the objectId exists
970
                IdentifierManager im = IdentifierManager.getInstance();
971
                if (im.identifierExists(guid)) {
972
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
973
                }
974

    
975
                logMetacat.debug("Commence creation...");
976
                IBindingFactory bfact =
977
                    BindingDirectory.getFactory(SystemMetadata.class);
978
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
979
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
980

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

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

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

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

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

    
1176
}
(2-2/3)