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

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

    
708
            DBQuery metacat = new DBQuery();
709

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

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

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

    
727
            System.out
728
                    .println(EcogridResultsetTransformer.toXMLString(records));
729
            response.setContentType("text/xml");
730
            out = response.getWriter();
731
            out.print(EcogridResultsetTransformer.toXMLString(records));
732

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

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

    
796
                logMetacat.debug("Commence creation...");
797
                AuthToken token = null;
798
                IBindingFactory bfact =
799
                    BindingDirectory.getFactory(SystemMetadata.class);
800
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
801
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
802

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

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

    
944
    /**
945
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
946
     * 
947
     * @throws IOException
948
     */
949
    private void login() throws IOException {
950
        PrintWriter out = response.getWriter();
951
        handler.handleLoginAction(out, params, request, response);
952
        out.close();
953
    }
954

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

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

    
997
}
(2-2/3)