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 != null && params.get(FUNCTION_KEYWORD) != null &&
271
                           params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA))
272
                        { //generate system metadata for any object that is
273
                          //a) not system metadata itself
274
                          //b) does not already have a system metadata id in the systemmetadata table
275
                          //c) not a BIN object (data)
276
                            generateMissingSystemMetadata();
277
                            status = true;
278
                        }
279
                        else
280
                        {
281
                            loadSessionData();
282
                            String objectId = request.getPathInfo();
283
                            if (objectId != null && objectId.length() > 1) 
284
                            {
285
                                objectId = request.getPathInfo().substring(1);
286
                            }
287
                            getSystemMetadataObject(objectId);
288
                            status = true;
289
                        }
290
                            
291
                    } else if (resource.equals(RESOURCE_OBJECTS)) {
292
                    logMetacat.debug("D1 Rest: Starting resource processing...");
293
                    loadSessionData();
294

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

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

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

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

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

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

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

    
369
        String name = null;
370
        String[] value = null;
371
        Enumeration paramlist = request.getParameterNames();
372
        while (paramlist.hasMoreElements()) {
373
            name = (String) paramlist.nextElement();
374
            value = request.getParameterValues(name);
375
            params.put(name, value);
376
        }
377
    }
378

    
379
    /**
380
     * 
381
     * Load user details of metacat session from the request 
382
     * 
383
     */
384
    private void loadSessionData()
385
      throws Exception
386
    {
387
        SessionData sessionData = RequestUtil.getSessionData(request);
388
        try
389
        {
390
            username = null;
391
            password = null;
392
            groupNames = null;
393
            sessionId = null;
394
            
395
            boolean validSession = false;
396
            SessionService ss = SessionService.getInstance();
397
            //validate the session
398
            if(ss.isSessionRegistered(sessionData.getId()))
399
            {
400
                validSession = true;
401
            }
402
            
403
            if(validSession)
404
            {
405
                //if the session is valid, set these variables
406
                username = sessionData.getUserName();
407
                password = sessionData.getPassword();
408
                groupNames = sessionData.getGroupNames();
409
                sessionId = sessionData.getId();
410
                System.out.println("setting sessionid to " + sessionId);
411
                System.out.println("username: " + username);
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
                System.out.println("setting username to public.");
419
                username = "public";
420
            }
421
        }
422
        catch(Exception e)
423
        {
424
            throw new Exception("Could not load the session data: " + e.getMessage());
425
        }
426
    }
427
    
428
    /**
429
     * generate missing system metadata for any science metadata objects
430
     * that don't already have it. https://trac.dataone.org/ticket/591
431
     * 
432
     * called with POST meta/?op=generatemissingsystemmetadata
433
     */
434
    private void generateMissingSystemMetadata()
435
    {
436
        AuthToken token = new AuthToken(sessionId);
437
        CrudService.getInstance().generateMissingSystemMetadata(token);
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 = new AuthToken(sessionId);
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
        OutputStream out = null;
751
        try {
752
            out = response.getOutputStream();
753
        } catch (IOException e1) {
754
            logMetacat.error("Could not get the output stream for writing in putObject");
755
        }
756
        try {
757
            
758
            // Read the incoming data from its Mime Multipart encoding
759
            logMetacat.debug("Disassembling MIME multipart form");
760
            InputStream object = null;
761
            InputStream sysmeta = null;
762
            MimeMultipart mmp = new MimeMultipart(new InputStreamDataSource("message", request.getInputStream()));
763
            logMetacat.debug("MMP created.");
764
            mmp.writeTo(System.out);
765
            for (int i = 0; i < mmp.getCount(); i++) {
766
                BodyPart part = mmp.getBodyPart(i);
767
                String name = part.getFileName();
768
                logMetacat.debug("Part name is: " + name);
769
                logMetacat.debug("Part has class name: " + part.getClass().getName());
770
                if (name.equals("object")) {
771
                    object = part.getInputStream();
772
                    logMetacat.debug("Found object part, size is: " + part.getSize());
773
                } else if (name.equals("systemmetadata")) {
774
                    sysmeta = part.getInputStream();
775
                    logMetacat.debug("Found sysmeta part, size is: " + part.getSize());
776
                } else {
777
                    throw new InvalidRequest("1000", "Request had malformed MIME part with name: " + name);
778
                }
779
            }
780
            
781
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
782

    
783
                // Check if the objectId exists
784
                IdentifierManager im = IdentifierManager.getInstance();
785
                if (im.identifierExists(guid)) {
786
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
787
                }
788

    
789
                logMetacat.debug("Commence creation...");
790
                IBindingFactory bfact =
791
                    BindingDirectory.getFactory(SystemMetadata.class);
792
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
793
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
794

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

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

    
937
    /**
938
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
939
     * 
940
     * @throws IOException
941
     */
942
    private void login() throws IOException {
943
        PrintWriter out = response.getWriter();
944
        handler.handleLoginAction(out, params, request, response);
945
        out.close();
946
    }
947

    
948
    /**
949
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
950
     * 
951
     * @throws IOException
952
     */
953
    private void logout() throws IOException {
954
        PrintWriter out = response.getWriter();
955
        handler.handleLogoutAction(out, params, request, response);
956
        out.close();
957
    }
958

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

    
990
}
(2-2/3)