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

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

    
56
import edu.ucsb.nceas.metacat.DBUtil;
57
import edu.ucsb.nceas.metacat.IdentifierManager;
58
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
59
import edu.ucsb.nceas.metacat.MetacatHandler;
60
import edu.ucsb.nceas.metacat.dataone.CrudService;
61
import edu.ucsb.nceas.metacat.service.SessionService;
62
import edu.ucsb.nceas.metacat.util.RequestUtil;
63
import edu.ucsb.nceas.metacat.util.SessionData;
64

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

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

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

    
177
    /**HTTP Verb GET*/
178
    public static final byte GET = 1;
179
    /**HTTP Verb POST*/
180
    public static final byte POST = 2;
181
    /**HTTP Verb PUT*/
182
    public static final byte PUT = 3;
183
    /**HTTP Verb DELETE*/
184
    public static final byte DELETE = 4;
185

    
186
    /*
187
     * API Resources
188
     */
189
    private static final String RESOURCE_OBJECTS = "object";
190
    private static final String RESOURCE_META = "meta";
191
    private static final String RESOURCE_SESSION = "session";
192
    private static final String RESOURCE_IDENTIFIER = "identifier";
193

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

    
209
    private ServletContext servletContext;
210
    private Logger logMetacat;
211
    private MetacatHandler handler;
212
    private HttpServletRequest request;
213
    private HttpServletResponse response;
214
    private String username;
215
    private String password;
216
    private String sessionId;
217
    private String[] groupNames;
218

    
219
    private Hashtable<String, String[]> params;
220

    
221
    /**Initializes new instance by setting servlet context,request and response*/
222
    public ResourceHandler(ServletContext servletContext,
223
            HttpServletRequest request, HttpServletResponse response) {
224
        this.servletContext = servletContext;
225
        this.request = request;
226
        this.response = response;
227
    }
228

    
229
    /**
230
     * This function is called from REST APU servlet and handles each request to the servlet 
231
     * 
232
     * @param httpVerb (GET, POST, PUT or DELETE)
233
     */
234
    public void handle(byte httpVerb) {
235
        logMetacat = Logger.getLogger(ResourceHandler.class);
236
        try {
237
            String resource = request.getServletPath();
238
            String verb = "";
239
            
240
            //System.out.println("handling verb " + httpVerb + " request with resource " + resource);
241
            boolean status = false;
242

    
243
            if (resource != null) {
244
                resource = request.getServletPath().substring(1);
245

    
246
                params = new Hashtable<String, String[]>();
247
                initParams();
248

    
249
                Timer timer = new Timer();
250
                handler = new MetacatHandler(timer);
251

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

    
294
                    String objectId = request.getPathInfo();
295
                    if (objectId != null && objectId.length() > 1) 
296
                    {
297
                        objectId = request.getPathInfo().substring(1);
298
                    }
299
                    else
300
                    {
301
                        objectId = null;
302
                    }
303
                    
304
                    logMetacat.debug("verb:" + httpVerb);
305

    
306
                    if (httpVerb == GET) {
307
                        getObject(objectId);
308
                        status = true;
309
                    } else if (httpVerb == POST) {
310
                        putObject(objectId, FUNCTION_NAME_INSERT);
311
                        status = true;
312
                    } else if (httpVerb == PUT) {
313
                        putObject(objectId, FUNCTION_NAME_UPDATE);
314
                        status = true;
315
                    } else if (httpVerb == DELETE) {
316
                        deleteObject(objectId);
317
                        status = true;
318
                    }
319

    
320
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
321

    
322
                    String identifierId = request.getPathInfo();
323
                    if (identifierId != null && identifierId.length() > 1)
324
                        identifierId = request.getPathInfo().substring(1); //trim the slash
325

    
326
                    if (httpVerb == GET) {
327
                        String op = params.get(FUNCTION_KEYWORD)[0];
328
                        if (op.equals(FUNCTION_NAME_ISREGISTERED)) {
329
                            isRegistered(identifierId);
330
                            status = true;
331
                        } else if (op.equals(FUNCTION_NAME_GETALLDOCS)) {
332
                            getAllDocIds();
333
                            status = true;
334
                        } else if (op.equals(FUNCTION_NAME_GETNEXTREV)) {
335
                            getNextRevision(identifierId);
336
                            status = true;
337
                        } else if (op.equals(FUNCTION_NAME_GETNEXTOBJ)) {
338
                            getNextObject();
339
                            status = true;
340
                        } 
341

    
342
                    } else if (httpVerb == PUT) {
343
                        //Earthgrid API > Identifier Service > addLSID Function 
344
                        printError(
345
                                "This method is not supported by metacat.  To "
346
                                        + "add a new LSID, add a document to metacat.",
347
                                response);
348
                        status = true;
349
                    }
350

    
351
                }
352
                if (!status)
353
                    printError("Incorrect parameters!", response);
354
            } else {
355
                printError("Incorrect resource!", response);
356
            }
357
        } catch (Exception e) {
358
            logMetacat.error(e.getMessage());
359
            e.printStackTrace();
360
        }
361
    }
362
    
363
    /**
364
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions  
365
     */
366
    private void initParams() {
367

    
368
        String name = null;
369
        String[] value = null;
370
        Enumeration paramlist = request.getParameterNames();
371
        while (paramlist.hasMoreElements()) {
372
            name = (String) paramlist.nextElement();
373
            value = request.getParameterValues(name);
374
            //System.out.println("adding param: " + name + " = " + value);
375
            params.put(name, value);
376
        }
377

    
378
    }
379

    
380
    /**
381
     * 
382
     * Load user details of metacat session from the request 
383
     * 
384
     */
385
    private void loadSessionData()
386
      throws Exception
387
    {
388
        SessionData sessionData = RequestUtil.getSessionData(request);
389
        try
390
        {
391
            username = null;
392
            password = null;
393
            groupNames = null;
394
            sessionId = null;
395
            
396
            boolean validSession = false;
397
            SessionService ss = SessionService.getInstance();
398
            //validate the session
399
            if(ss.isSessionRegistered(sessionData.getId()))
400
            {
401
                validSession = true;
402
            }
403
            
404
            if(validSession)
405
            {
406
                //if the session is valid, set these variables
407
                username = sessionData.getUserName();
408
                password = sessionData.getPassword();
409
                groupNames = sessionData.getGroupNames();
410
                sessionId = sessionData.getId();
411
                //System.out.println("setting sessionid to " + sessionId);
412
            }
413
            
414
            //if the session is not valid or the username is null, set
415
            //username to public
416
            if (username == null) 
417
            {
418
                username = "public";
419
            }
420
        }
421
        catch(Exception e)
422
        {
423
            throw new Exception("Could not load the session data: " + e.getMessage());
424
        }
425
    }
426
    
427
    /**
428
     * generate missing system metadata for any science metadata objects
429
     * that don't already have it. https://trac.dataone.org/ticket/591
430
     */
431
    private void generateMissingSystemMetadata()
432
    {
433
        //generate system metadata for any object that is
434
        //a) not system metadata itself
435
        //b) does not already have a system metadata id in the systemmetadata table
436
        //c) not a BIN object (data)
437
        
438
    }
439

    
440
    /**
441
     *  Earthgrid API > Identifier Service > isRegistered Function : 
442
     *  calls MetacatHandler > handleIdIsRegisteredAction
443
     * @param guid
444
     * @throws IOException
445
     */
446
    private void isRegistered(String guid) throws IOException
447
    {
448
        
449
        // Look up the localId for this guid
450
        IdentifierManager im = IdentifierManager.getInstance();
451
        String localId = "";
452
        try {
453
            localId = im.getLocalId(guid);
454
        } catch (McdbDocNotFoundException e) {
455
            // TODO: Need to return the proper DataONE exception
456
        }
457
        
458
        params.put("docid", new String[] { localId });
459
        PrintWriter out = response.getWriter();
460
        handler.handleIdIsRegisteredAction(out, params, response);
461
        out.close();
462
    }
463

    
464
    /**
465
     * Earthgrid API > Identifier Service > getAllDocIds Function : 
466
     * calls MetacatHandler > handleGetAllDocidsAction
467
     * @throws IOException
468
     */
469
    private void getAllDocIds() throws IOException {
470
        PrintWriter out = response.getWriter();
471
        handler.handleGetAllDocidsAction(out, params, response);
472
        out.close();
473
    }
474

    
475
    /**
476
     * Earthgrid API > Identifier Service > getNextRevision Function : 
477
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
478
     * @param guid
479
     * @throws IOException
480
     */
481
    private void getNextRevision(String guid) throws IOException 
482
    {
483
        params.put("docid", new String[] { guid });
484
        PrintWriter out = response.getWriter();
485
        //handler.handleGetRevisionAndDocTypeAction(out, params);
486

    
487
        try {
488
            // Make sure there is a docid
489
            if (guid == null || guid.equals("")) {
490
                throw new Exception("User didn't specify docid!");
491
            }
492

    
493
            // Look up the localId for this guid
494
            IdentifierManager im = IdentifierManager.getInstance();
495
            String localId = "";
496
            try {
497
                localId = im.getLocalId(guid);
498
            } catch (McdbDocNotFoundException e) {
499
                // TODO: Need to return the proper DataONE exception
500
            }
501
           
502
            // Create a DBUtil object
503
            DBUtil dbutil = new DBUtil();
504
            // Get a rev and doctype
505
            String revAndDocType = dbutil
506
                    .getCurrentRevisionAndDocTypeForGivenDocument(localId);
507
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
508

    
509
            out.println("<?xml version=\"1.0\"?>");
510
            out.print("<next-revision>");
511
            out.print(revision);
512
            out.print("</next-revision>");
513

    
514
        } catch (Exception e) {
515
            // Handle exception
516
            out.println("<?xml version=\"1.0\"?>");
517
            out.println("<error>");
518
            out.println(e.getMessage());
519
            out.println("</error>");
520
        }
521

    
522
        out.close();
523
    }
524

    
525
    /**
526
     * Earthgrid API > Identifier Service > getNextObject Function : 
527
     * calls MetacatHandler > handleGetMaxDocidAction
528
     * @throws IOException
529
     */
530
    private void getNextObject() throws IOException {
531
        PrintWriter out = response.getWriter();
532
        handler.handleGetMaxDocidAction(out, params, response);
533
        out.close();
534
    }
535

    
536
    /**
537
     * Implements REST version of DataONE CRUD API --> get
538
     * @param guid ID of data object to be read
539
     */
540
    private void getObject(String guid) {
541
      //hack...fix this
542
        //CrudService cs = new CrudService(servletContext, request, response);
543
        CrudService cs = CrudService.getInstance();
544
        cs.setParamsFromRequest(request);
545
        //System.out.println("sessionId: " + sessionId);
546
        AuthToken token = new AuthToken(sessionId);
547
        OutputStream out = null;
548
        try {
549
            out = response.getOutputStream();
550
            if(guid != null)
551
            { //get a specific document
552
                Identifier id = new Identifier();
553
                id.setValue(guid);
554
                InputStream data = cs.get(token, id);
555
                IOUtils.copyLarge(data, response.getOutputStream());
556
            }
557
            else
558
            { //call listObjects with specified params
559
                Date startTime = null;
560
                Date endTime = null;
561
                ObjectFormat objectFormat = null;
562
                boolean replicaStatus = false;
563
                int start = 0;
564
                int count = 1000;
565
                Enumeration paramlist = request.getParameterNames();
566
                while (paramlist.hasMoreElements()) 
567
                { //parse the params and make the crud call
568
                    String name = (String) paramlist.nextElement();
569
                    String[] value = (String[])request.getParameterValues(name);
570
                    if(name.equals("startTime"))
571
                    {
572
                        try
573
                        {
574
                          startTime = DateFormat.getDateTimeInstance().parse(value[0]);
575
                        }
576
                        catch(Exception e)
577
                        {  //if we can't parse it, just don't use the startTime param
578
                            System.out.println("Could not parse startTime: " + value[0]);
579
                            startTime = null;
580
                        }
581
                    }
582
                    else if(name.equals("endTime"))
583
                    {
584
                        try
585
                        {
586
                          endTime = DateFormat.getDateTimeInstance().parse(value[0]);
587
                        }
588
                        catch(Exception e)
589
                        {  //if we can't parse it, just don't use the endTime param
590
                            System.out.println("Could not parse endTime: " + value[0]);
591
                            endTime = null;
592
                        }
593
                    }
594
                    else if(name.equals("objectFormat"))
595
                    {
596
                        objectFormat = ObjectFormat.convert(value[0]);
597
                    }
598
                    else if(name.equals("replicaStatus"))
599
                    {
600
                        if(value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES"))
601
                        {
602
                            replicaStatus = true;
603
                        }
604
                    }
605
                    else if(name.equals("start"))
606
                    {
607
                        start = new Integer(value[0]).intValue();
608
                    }
609
                    else if(name.equals("count"))
610
                    {
611
                        count = new Integer(value[0]).intValue();
612
                    }
613
                }
614
                //make the crud call
615
                /*System.out.println("token: " + token + " startTime: " + startTime +
616
                        " endtime: " + endTime + " objectFormat: " + 
617
                        objectFormat + " replicaStatus: " + replicaStatus + 
618
                        " start: " + start + " count: " + count);
619
                */
620
                ObjectList ol = cs.listObjects(token, startTime, endTime, 
621
                        objectFormat, replicaStatus, start, count);
622
                ol = cs.listObjects(token, null, null, null, false, 0, 1000);
623
                
624
                //StringReader sr = new StringReader(ol.toString());
625
                //IOUtils.copy(sr, response.getOutputStream());
626
                
627
                out = response.getOutputStream();                
628
                // Serialize and write it to the output stream
629
                try {
630
                    IBindingFactory bfact = BindingDirectory.getFactory(ObjectList.class);
631
                    IMarshallingContext mctx = bfact.createMarshallingContext();
632
                    mctx.marshalDocument(ol, "UTF-8", null, out);
633
                } catch (JiBXException e) {
634
                    throw new ServiceFailure("1190", "Failed to serialize ObjectList: " + e.getMessage());
635
                }
636
            }
637
        } catch (BaseException e) {
638
                serializeException(e, out);
639
        } catch (IOException e) {
640
            ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
641
            serializeException(sf, out);
642
        }
643
    }
644

    
645
    /**
646
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
647
     * @param guid ID of data object to be read
648
     */
649
    private void getSystemMetadataObject(String guid) {
650
        CrudService cs = CrudService.getInstance();
651
        cs.setParamsFromRequest(request);
652
        AuthToken token = null;
653
        OutputStream out = null;
654
        try {
655
            out = response.getOutputStream();
656
            Identifier id = new Identifier();
657
            id.setValue(guid);
658
            SystemMetadata sysmeta = cs.getSystemMetadata(token, id);
659
            
660
            // Serialize and write it to the output stream
661
            try {
662
                IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
663
                IMarshallingContext mctx = bfact.createMarshallingContext();
664
                mctx.marshalDocument(sysmeta, "UTF-8", null, out);
665
            } catch (JiBXException e) {
666
                throw new ServiceFailure("1190", "Failed to serialize SystemMetadata: " + e.getMessage());
667
            }
668
        } catch (BaseException e) {
669
                serializeException(e, out);
670
        } catch (IOException e) {
671
            ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
672
            serializeException(sf, out);
673
        } finally {
674
            IOUtils.closeQuietly(out);
675
        }
676
    }
677
    
678
    /**
679
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query 
680
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
681
     * 
682
     * NOTE:
683
     *      This is the only method that uses EcoGrid classes for its implementation.  
684
     *      It does so because it takes an EcoGrid Query as input, and outputs an
685
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
686
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
687
     *      This creates a circular dependency, because the Metacat classes are needed
688
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
689
     *      method.  This circularity could be resolved by moving the EcoGrid classes
690
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
691
     *      favor of these REST interfaces, this circular dependency can be eliminated.
692
     *        
693
     * @throws Exception
694
     */
695
    private void query() throws Exception {
696
        /*  This block commented out because of the EcoGrid circular dependency.
697
         *  For now, query will not be supported until the circularity can be
698
         *  resolved, probably by moving the ecogrid query syntax transformers
699
         *  directly into the Metacat codebase.  MBJ 2010-02-03
700
         
701
        try {
702
            EcogridQueryParser parser = new EcogridQueryParser(request
703
                    .getReader());
704
            parser.parseXML();
705
            QueryType queryType = parser.getEcogridQuery();
706
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
707
                new EcogridJavaToMetacatJavaQueryTransformer();
708
            QuerySpecification metacatQuery = queryTransformer
709
                    .transform(queryType);
710

    
711
            DBQuery metacat = new DBQuery();
712

    
713
            boolean useXMLIndex = (new Boolean(PropertyService
714
                    .getProperty("database.usexmlindex"))).booleanValue();
715
            String xmlquery = "query"; // we don't care the query in resultset,
716
            // the query can be anything
717
            PrintWriter out = null; // we don't want metacat result, so set out null
718

    
719
            // parameter: queryspecification, user, group, usingIndexOrNot
720
            StringBuffer result = metacat.createResultDocument(xmlquery,
721
                    metacatQuery, out, username, groupNames, useXMLIndex);
722

    
723
            // create result set transfer		
724
            String saxparser = PropertyService.getProperty("xml.saxparser");
725
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
726
                    new StringReader(result.toString()), saxparser, queryType
727
                            .getNamespace().get_value());
728
            ResultsetType records = metacatResultsetParser.getEcogridResult();
729

    
730
            System.out
731
                    .println(EcogridResultsetTransformer.toXMLString(records));
732
            response.setContentType("text/xml");
733
            out = response.getWriter();
734
            out.print(EcogridResultsetTransformer.toXMLString(records));
735

    
736
        } catch (Exception e) {
737
            e.printStackTrace();
738
        }*/
739
        response.setContentType("text/xml");
740
        PrintWriter out = response.getWriter();
741
        out.print("<error>Query operation not yet supported by Metacat.</error>");
742
        out.close();
743
    }
744
    
745
    /**
746
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
747
     * 
748
     * @param guid ID of data object to be inserted or updated
749
     * @throws IOException
750
     */
751
    private void putObject(String guid, String action) {
752
        logMetacat.debug("Entering putObject: " + guid + "/" + action);
753
        
754
        // TODO: This function lacks proper handling of authz and authn, so it
755
        // seems that anyone can insert or update; interacts with 
756
        // loadSessionData(), which doesn't validate the session
757
        
758
        // Get an output stream for handling errors; this should really be passed in as
759
        // a parameter
760
        OutputStream out = null;
761
        try {
762
            out = response.getOutputStream();
763
        } catch (IOException e1) {
764
            logMetacat.error("Could not get the output stream for writing in putObject");
765
        }
766
        try {
767
            
768
            // Read the incoming data from its Mime Multipart encoding
769
            logMetacat.debug("Disassembling MIME multipart form");
770
            InputStream object = null;
771
            InputStream sysmeta = null;
772
            MimeMultipart mmp = new MimeMultipart(new InputStreamDataSource("message", request.getInputStream()));
773
            logMetacat.debug("MMP created.");
774
            mmp.writeTo(System.out);
775
            for (int i = 0; i < mmp.getCount(); i++) {
776
                BodyPart part = mmp.getBodyPart(i);
777
                String name = part.getFileName();
778
                logMetacat.debug("Part name is: " + name);
779
                logMetacat.debug("Part has class name: " + part.getClass().getName());
780
                if (name.equals("object")) {
781
                    object = part.getInputStream();
782
                    logMetacat.debug("Found object part, size is: " + part.getSize());
783
                } else if (name.equals("systemmetadata")) {
784
                    sysmeta = part.getInputStream();
785
                    logMetacat.debug("Found sysmeta part, size is: " + part.getSize());
786
                } else {
787
                    throw new InvalidRequest("1000", "Request had malformed MIME part with name: " + name);
788
                }
789
            }
790
            
791
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
792

    
793
                // Check if the objectId exists
794
                IdentifierManager im = IdentifierManager.getInstance();
795
                if (im.identifierExists(guid)) {
796
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
797
                }
798

    
799
                // TODO: access control -- should be in CrudService et al. I think
800
                //if (username != null && !username.equals("public")) {
801
                if (username != null) {
802
                    logMetacat.debug("Commence creation...");
803
                    AuthToken token = null;
804
                    IBindingFactory bfact =
805
                        BindingDirectory.getFactory(SystemMetadata.class);
806
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
807
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
808
                    
809
                    CrudService cs = CrudService.getInstance();
810
                    cs.setParamsFromRequest(request);
811
                    Identifier id = new Identifier();
812
                    id.setValue(guid);
813
                    cs.create(token, id, object, m);
814

    
815
                } else {
816
                    logMetacat.debug("Unauthorized to create.");
817
                    throw new NotAuthorized("1000", "Permission denied for user " + username);
818
                }
819
            } else if (action.equals(FUNCTION_NAME_UPDATE)) { //handle updates
820
                IdentifierManager im = IdentifierManager.getInstance();
821
                CrudService cs = CrudService.getInstance();
822
                Identifier obsoletedGuid = new Identifier();
823
                Identifier id = new Identifier();
824
                id.setValue(guid);
825
                AuthToken token = null;
826
                
827
                //do some checks
828
                if(params.get("obsoletedGuid") == null)
829
                {
830
                    throw new InvalidRequest("1202", "obsoletedGuid must be contained in the request parameters.");
831
                }
832
                //get the obsoletedGuid
833
                String[] obsGuidS = params.get("obsoletedGuid");
834
                obsoletedGuid.setValue(obsGuidS[0]);
835
                
836
                if (!im.identifierExists(obsoletedGuid.getValue())) 
837
                {
838
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
839
                }
840
                
841
                
842
                logMetacat.debug("Commence update...");
843
                
844
                //get the systemmetadata
845
                IBindingFactory bfact =
846
                        BindingDirectory.getFactory(SystemMetadata.class);
847
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
848
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
849
                
850
                //do the update
851
                try
852
                {
853
                    cs.setParamsFromRequest(request);
854
                    Identifier rId = cs.update(token, id, object, obsoletedGuid, m);
855
                }
856
                catch(NotFound e)
857
                {
858
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
859
                }
860
                
861
            } else {
862
                throw new InvalidRequest("1000", "Operation must be create or update.");
863
            }
864
        } catch (NotAuthorized e) {
865
            serializeException(e, out);
866
        } catch (InvalidToken e) {
867
            serializeException(e, out);
868
        } catch (ServiceFailure e) {
869
            serializeException(e, out);
870
        } catch (IdentifierNotUnique e) {
871
            serializeException(e, out);
872
        } catch (UnsupportedType e) {
873
            serializeException(e, out);
874
        } catch (InsufficientResources e) {
875
            serializeException(e, out);
876
        } catch (InvalidSystemMetadata e) {
877
            serializeException(e, out);
878
        } catch (NotImplemented e) {
879
            serializeException(e, out);
880
        } catch (InvalidRequest e) {
881
            serializeException(e, out);
882
        } catch (MessagingException e) {
883
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
884
            serializeException(sf, out);
885
        } catch (IOException e) {
886
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
887
            serializeException(sf, out);
888
        } catch (JiBXException e) {
889
            e.printStackTrace(System.out);
890
            InvalidSystemMetadata ism = new InvalidSystemMetadata("1080", e.getMessage());
891
            serializeException(ism, out);
892
        }
893
    }
894

    
895
    /**
896
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction  
897
     * 
898
     * @param guid ID of data object to be deleted
899
     * @throws IOException
900
     */
901
    private void deleteObject(String guid) throws IOException 
902
    {
903
        // Look up the localId for this global identifier
904
        IdentifierManager im = IdentifierManager.getInstance();
905
        String localId = "";
906
        try {
907
            localId = im.getLocalId(guid);
908
        } catch (McdbDocNotFoundException e) {
909
            // TODO: Need to return the proper DataONE exception
910
        }
911
        
912
        params.put("docid", new String[] { localId });
913
        PrintWriter out = response.getWriter();
914
        handler.handleDeleteAction(out, params, request, response, username,
915
                groupNames);
916
        out.close();
917
    }
918
    
919
    /**
920
     * set the access perms on a document
921
     * @throws IOException
922
     */
923
    private void setaccess() throws IOException
924
    {
925
        try
926
        {
927
            String guid = params.get("guid")[0];
928
            Identifier id = new Identifier();
929
            id.setValue(guid);
930
            AuthToken token = new AuthToken(sessionId);
931
            String principal = params.get("principal")[0];
932
            String permission = params.get("permission")[0];
933
            String permissionType = params.get("permissionType")[0];
934
            String permissionOrder = params.get("permissionOrder")[0];
935
            String setSystemMetadata = params.get("setsystemmetadata")[0];
936
            CrudService cs = CrudService.getInstance();
937
            cs.setAccess(token, id, principal, permission, permissionType, permissionOrder);
938
            if(setSystemMetadata.equals("true") || setSystemMetadata.equals("TRUE") ||
939
               setSystemMetadata.equals("yes"))
940
            { //set the same perms on the system metadata doc
941
                IdentifierManager im = IdentifierManager.getInstance();
942
                String smidS = im.getSystemMetadataId(id.getValue());
943
                Identifier smid = new Identifier();
944
                smid.setValue(smidS);
945
                cs.setAccess(token, smid, "public", "read", "allow", "allowFirst");
946
            }
947
        }
948
        catch(Exception e)
949
        {
950
            printError("Error setting access in ResourceHandler: " + e.getMessage(), response);
951
        }
952
    }
953

    
954
    /**
955
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
956
     * 
957
     * @throws IOException
958
     */
959
    private void login() throws IOException {
960
        PrintWriter out = response.getWriter();
961
        handler.handleLoginAction(out, params, request, response);
962
        out.close();
963
    }
964

    
965
    /**
966
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
967
     * 
968
     * @throws IOException
969
     */
970
    private void logout() throws IOException {
971
        PrintWriter out = response.getWriter();
972
        handler.handleLogoutAction(out, params, request, response);
973
        out.close();
974
    }
975

    
976
    /**
977
     * Prints xml response
978
     * @param message Message to be displayed
979
     * @param response Servlet response that xml message will be printed 
980
     * */
981
    private void printError(String message, HttpServletResponse response) {
982
        try {
983
            PrintWriter out = response.getWriter();
984
            response.setContentType("text/xml");
985
            out.println("<?xml version=\"1.0\"?>");
986
            out.println("<error>");
987
            out.println(message);
988
            out.println("</error>");
989
            out.close();
990
        } catch (IOException e) {
991
            e.printStackTrace();
992
        }
993
    }
994
    
995
    private void serializeException(BaseException e, OutputStream out) {
996
        // TODO: Use content negotiation to determine which return format to use
997
        response.setContentType("text/xml");
998
        response.setStatus(e.getCode());
999
        try {
1000
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
1001
        } catch (IOException e1) {
1002
            logMetacat.error("Error writing exception to stream. " 
1003
                    + e1.getMessage());
1004
        }
1005
    }
1006

    
1007
}
(2-2/3)