Project

General

Profile

1 5211 jones
/**
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 5370 berkley
import java.io.*;
26
import java.util.*;
27 5211 jones
28 5319 jones
import javax.mail.BodyPart;
29
import javax.mail.MessagingException;
30
import javax.mail.internet.MimeMultipart;
31 5211 jones
import javax.servlet.ServletContext;
32
import javax.servlet.http.HttpServletRequest;
33
import javax.servlet.http.HttpServletResponse;
34 5370 berkley
import java.text.DateFormat;
35 5391 berkley
import java.text.ParsePosition;
36
import java.text.SimpleDateFormat;
37 5211 jones
38 5391 berkley
39 5390 berkley
import org.apache.commons.httpclient.util.DateParser;
40 5299 jones
import org.apache.commons.io.IOUtils;
41 5211 jones
import org.apache.log4j.Logger;
42 5299 jones
import org.dataone.service.exceptions.BaseException;
43 5319 jones
import org.dataone.service.exceptions.IdentifierNotUnique;
44
import org.dataone.service.exceptions.InsufficientResources;
45
import org.dataone.service.exceptions.InvalidRequest;
46
import org.dataone.service.exceptions.InvalidSystemMetadata;
47
import org.dataone.service.exceptions.InvalidToken;
48
import org.dataone.service.exceptions.NotAuthorized;
49
import org.dataone.service.exceptions.NotImplemented;
50 5299 jones
import org.dataone.service.exceptions.ServiceFailure;
51 5319 jones
import org.dataone.service.exceptions.UnsupportedType;
52 5353 berkley
import org.dataone.service.exceptions.NotFound;
53 5370 berkley
import org.dataone.service.types.*;
54 5320 jones
import org.jibx.runtime.BindingDirectory;
55
import org.jibx.runtime.IBindingFactory;
56 5332 jones
import org.jibx.runtime.IMarshallingContext;
57 5320 jones
import org.jibx.runtime.IUnmarshallingContext;
58
import org.jibx.runtime.JiBXException;
59 5211 jones
60
import edu.ucsb.nceas.metacat.DBUtil;
61 5282 jones
import edu.ucsb.nceas.metacat.IdentifierManager;
62
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
63 5211 jones
import edu.ucsb.nceas.metacat.MetacatHandler;
64 5299 jones
import edu.ucsb.nceas.metacat.dataone.CrudService;
65 5374 berkley
import edu.ucsb.nceas.metacat.service.SessionService;
66 5211 jones
import edu.ucsb.nceas.metacat.util.RequestUtil;
67
import edu.ucsb.nceas.metacat.util.SessionData;
68
69
/**
70
 *
71
 * Implements Earthgrid REST API to Metacat <br/><br/>
72
 *
73
 * <ul>
74
 * <li>
75
 * <h3> EarthGrid Query Service</h3>
76
 * <ul><li>
77
 * <h3>Get & Authenticated Get:</h3>
78
 * is equal to Metacat's read action and returns a data file having
79
 * the specified <doc-id> in the resource path. For authenticated Get service, a session id must be provided
80
 * in the query string. <br/><br/>
81
 *
82 5224 jones
 * <b>REST URL:</b>	<code>GET, [context-root]/object/[doc-id]?sessionid=[sessionid] </code><br/>
83 5211 jones
 * <b>Returns:</b> data file <br/><br/>
84
 * </li>
85
 *
86
 * <li>
87
 * <h3>Query & Authenticated Query:</h3>
88
 * Metacat's equivalent is squery action but this function
89
 * receives a Earthgrid query document and returns Earthgrid resultset document by transforming those documents
90
 * to Metacat's equivalents by the means of Metacat Implementation in Earthgrid library. For authenticated Query service
91
 * a session id must be provided in the query string. See Earthgrid (a.k.a. Ecogrid) project for XSD files of
92
 * query and resultset documents<br/><br/>
93
 *
94 5224 jones
 * <b>REST URL:</b>	<code>POST, [context-root]/object?sessionid=[sessionid]</code>    <br/>
95 5211 jones
 * <b>POST Data:</b> Earthgrid query document , Content-type: <code>text/xml</code><br/>
96
 * <b>Returns:</b> Earthgrid resultset document<br/><br/>
97
 *
98
 * </li>
99
 * </ul>
100
 *
101
 * </li>
102
 *
103
 * <li>
104
 * <h3> EarthGrid Authentication Service</h3>
105
 * <ul><li>
106
 * <h3>Login: </h3>
107
 * Receives username and password parameters in POST data and
108
 * returns SessionId (in XML format) or failure message and uses MetacatHandler's handleLoginAction function<br/><br/>
109
 *
110
 * <b>REST URL:</b> <code>POST, [context-root]/session?op=login</code> <br/>
111
 * <b>POST Data:</b> username=[username]&password=[password], Content-type: <code>application/x-www-form-urlencoded</code>
112
 * <b>Returns:</b> sessionId in XML format<br/><br/>
113
 * </li>
114
 *
115
 * <li>
116
 * <h3>Logout: </h3>
117
 * Receives session Id parameters in querystring and returns xml message, calls
118
 * MetacatHandler's handleLogoutAction function<br/><br/>
119
 *
120
 * <b>REST URL:</b>	<code>GET, [context-root]/session?op=logout&sessionid=[sessionid]</code>   <br/>
121
 * <b>Returns:</b> message in XML format<br/><br/>
122
 * </li>
123
 * </ul>
124
 *
125
 * <li>
126
 * <h3>EarthGrid Put Service</h3>
127
 *
128
 * <ul>
129
 * <li><h3>Update/Insert: </h3>
130
 * <br/>
131 5224 jones
 * <b>REST URL:</b>	<code>PUT, [context-root]/object/[doc-id]?op={update|insert}&sessionid=[sessionid]</code>   <br/>
132 5211 jones
 * <b>POST Data:</b> document object, Content-type: <code>text/xml</code><br/>
133
 * <b>Returns:</b> message in XML format<br/><br/>
134
 * </li>
135
 *
136
 * <li><h3>Delete: </h3>
137
 * <br/>
138 5224 jones
 * <b>REST URL:</b>	<code>DELETE, [context-root]/object/[doc-id]?sessionid=[sessionid]</code>   <br/>
139 5211 jones
 * <b>Returns:</b> message in XML format<br/><br/>
140
 * </li>
141
142
 * </ul>
143
 * </li>
144
 *
145
 * <li>
146
 * <h3>EarthGrid Identifier Service</h3><br/>
147
 *
148
 * <ul>
149
 * <li><h3>isRegistered: </h3>		<br/>
150
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=isregistered</code>   <br/>
151
 * <b>Returns:</b> message in XML format<br/><br/>
152
 * </li>
153
154
 * <li><h3>getAllDocIds:</h3>		<br/>
155
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getalldocids</code>   <br/>
156
 * <b>Returns:</b> document id list in XML format<br/><br/>
157
 * </li>
158
 *
159
 * <li><h3>addLSID Function:</h3>
160
 * Metacat does not support this function 		<br/>
161
 * <b>REST URL:</b>	<code>PUT, [context-root]/identifier/[doc-id]</code>   <br/>
162
 * <b>Returns:</b> error message in XML format<br/><br/>
163
 * </li>
164
 *
165
 * <li><h3>getNextRevision:</h3>		<br/>
166
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=getnextrevision</code>   <br/>
167
 * <b>Returns:</b> message in XML format<br/><br/>
168
 * </li>
169
 *
170
 * <li><h3>getNextObject:</h3>		<br/>
171
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getnextobject&scope=[scope]</code>   <br/>
172
 * <b>Returns:</b> message in XML format<br/><br/>
173
 * </li>
174
 *
175
 * </li>
176
 * </ul>
177
 *
178
 */
179
public class ResourceHandler {
180
181
    /**HTTP Verb GET*/
182
    public static final byte GET = 1;
183
    /**HTTP Verb POST*/
184
    public static final byte POST = 2;
185
    /**HTTP Verb PUT*/
186
    public static final byte PUT = 3;
187
    /**HTTP Verb DELETE*/
188
    public static final byte DELETE = 4;
189
190
    /*
191
     * API Resources
192
     */
193 5224 jones
    private static final String RESOURCE_OBJECTS = "object";
194 5355 berkley
    private static final String RESOURCE_META = "meta";
195 5211 jones
    private static final String RESOURCE_SESSION = "session";
196
    private static final String RESOURCE_IDENTIFIER = "identifier";
197 5390 berkley
    private static final String RESOURCE_LOG = "log";
198 5211 jones
199
    /*
200
     * API Functions used as URL parameters
201
     */
202
    private static final String FUNCTION_KEYWORD = "op";
203
    private static final String FUNCTION_NAME_LOGIN = "login";
204
    private static final String FUNCTION_NAME_LOGOUT = "logout";
205 5370 berkley
    private static final String FUNCTION_NAME_SET_ACCESS = "setaccess";
206 5211 jones
    private static final String FUNCTION_NAME_ISREGISTERED = "isregistered";
207
    private static final String FUNCTION_NAME_GETALLDOCS = "getalldocids";
208
    private static final String FUNCTION_NAME_GETNEXTREV = "getnextrevision";
209
    private static final String FUNCTION_NAME_GETNEXTOBJ = "getnextobject";
210
    private static final String FUNCTION_NAME_INSERT = "insert";
211
    private static final String FUNCTION_NAME_UPDATE = "update";
212 5374 berkley
    private static final String FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA = "generatemissingsystemmetadata";
213 5211 jones
214
    private ServletContext servletContext;
215
    private Logger logMetacat;
216
    private MetacatHandler handler;
217
    private HttpServletRequest request;
218
    private HttpServletResponse response;
219
    private String username;
220
    private String password;
221
    private String sessionId;
222
    private String[] groupNames;
223
224
    private Hashtable<String, String[]> params;
225
226
    /**Initializes new instance by setting servlet context,request and response*/
227
    public ResourceHandler(ServletContext servletContext,
228
            HttpServletRequest request, HttpServletResponse response) {
229
        this.servletContext = servletContext;
230
        this.request = request;
231
        this.response = response;
232
    }
233
234
    /**
235
     * This function is called from REST APU servlet and handles each request to the servlet
236
     *
237
     * @param httpVerb (GET, POST, PUT or DELETE)
238
     */
239
    public void handle(byte httpVerb) {
240
        logMetacat = Logger.getLogger(ResourceHandler.class);
241
        try {
242
            String resource = request.getServletPath();
243 5355 berkley
            String verb = "";
244 5370 berkley
245
            //System.out.println("handling verb " + httpVerb + " request with resource " + resource);
246 5211 jones
            boolean status = false;
247
248
            if (resource != null) {
249
                resource = request.getServletPath().substring(1);
250
251
                params = new Hashtable<String, String[]>();
252
                initParams();
253
254
                Timer timer = new Timer();
255 5337 berkley
                handler = new MetacatHandler(timer);
256 5211 jones
257 5355 berkley
                if (resource.equals(RESOURCE_SESSION) &&
258
                    httpVerb == POST &&
259
                    params.get(FUNCTION_KEYWORD) != null) {
260 5370 berkley
                    //System.out.println("function_keyword: " + params.get(FUNCTION_KEYWORD)[0]);
261
                    //System.out.println("function set access: " + FUNCTION_NAME_SET_ACCESS);
262
                        if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGIN)) {
263 5355 berkley
                            login();
264
                            status = true;
265 5370 berkley
                        } else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGOUT)) {
266 5355 berkley
                            logout();
267
                            status = true;
268 5370 berkley
                        } else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_SET_ACCESS)) {
269
                            //System.out.println("setting access in resourceHandler");
270
                            setaccess();
271
                            status = true;
272
                            //System.out.println("done setting access");
273 5355 berkley
                        }
274
                    } else if (resource.equals(RESOURCE_META)) {
275 5386 berkley
                        if(params != null && params.get(FUNCTION_KEYWORD) != null &&
276
                           params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA))
277 5374 berkley
                        { //generate system metadata for any object that is
278
                          //a) not system metadata itself
279
                          //b) does not already have a system metadata id in the systemmetadata table
280
                          //c) not a BIN object (data)
281
                            generateMissingSystemMetadata();
282
                            status = true;
283
                        }
284
                        else
285 5355 berkley
                        {
286 5374 berkley
                            loadSessionData();
287
                            String objectId = request.getPathInfo();
288
                            if (objectId != null && objectId.length() > 1)
289
                            {
290
                                objectId = request.getPathInfo().substring(1);
291
                            }
292
                            getSystemMetadataObject(objectId);
293
                            status = true;
294 5355 berkley
                        }
295
296
                    } else if (resource.equals(RESOURCE_OBJECTS)) {
297 5319 jones
                    logMetacat.debug("D1 Rest: Starting resource processing...");
298 5211 jones
                    loadSessionData();
299
300
                    String objectId = request.getPathInfo();
301 5355 berkley
                    if (objectId != null && objectId.length() > 1)
302
                    {
303
                        objectId = request.getPathInfo().substring(1);
304 5332 jones
                    }
305 5370 berkley
                    else
306
                    {
307
                        objectId = null;
308
                    }
309 5355 berkley
310 5319 jones
                    logMetacat.debug("verb:" + httpVerb);
311 5211 jones
312
                    if (httpVerb == GET) {
313 5355 berkley
                        getObject(objectId);
314 5211 jones
                        status = true;
315
                    } else if (httpVerb == POST) {
316 5319 jones
                        putObject(objectId, FUNCTION_NAME_INSERT);
317 5211 jones
                        status = true;
318
                    } else if (httpVerb == PUT) {
319 5319 jones
                        putObject(objectId, FUNCTION_NAME_UPDATE);
320 5211 jones
                        status = true;
321
                    } else if (httpVerb == DELETE) {
322
                        deleteObject(objectId);
323
                        status = true;
324
                    }
325
326
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
327
328
                    String identifierId = request.getPathInfo();
329
                    if (identifierId != null && identifierId.length() > 1)
330
                        identifierId = request.getPathInfo().substring(1); //trim the slash
331
332
                    if (httpVerb == GET) {
333
                        String op = params.get(FUNCTION_KEYWORD)[0];
334
                        if (op.equals(FUNCTION_NAME_ISREGISTERED)) {
335
                            isRegistered(identifierId);
336
                            status = true;
337
                        } else if (op.equals(FUNCTION_NAME_GETALLDOCS)) {
338
                            getAllDocIds();
339
                            status = true;
340
                        } else if (op.equals(FUNCTION_NAME_GETNEXTREV)) {
341
                            getNextRevision(identifierId);
342
                            status = true;
343
                        } else if (op.equals(FUNCTION_NAME_GETNEXTOBJ)) {
344
                            getNextObject();
345
                            status = true;
346 5370 berkley
                        }
347 5211 jones
348
                    } else if (httpVerb == PUT) {
349
                        //Earthgrid API > Identifier Service > addLSID Function
350
                        printError(
351
                                "This method is not supported by metacat.  To "
352
                                        + "add a new LSID, add a document to metacat.",
353
                                response);
354
                        status = true;
355
                    }
356
357 5390 berkley
                } else if (resource.equals(RESOURCE_LOG)) {
358
                    //handle log events
359
                    if(httpVerb == GET)
360
                    {
361
                        getLog();
362
                        status = true;
363
                    }
364
                    else
365
                    {
366
                        printError("POST, PUT, DELETE is not supported for logs.", response);
367
                        status = true;
368
                    }
369
370 5211 jones
                }
371 5390 berkley
372 5211 jones
                if (!status)
373
                    printError("Incorrect parameters!", response);
374
            } else {
375
                printError("Incorrect resource!", response);
376
            }
377
        } catch (Exception e) {
378
            logMetacat.error(e.getMessage());
379
            e.printStackTrace();
380
        }
381
    }
382 5374 berkley
383
    /**
384 5390 berkley
     * get the logs from the CrudService based on passed params.  Available
385
     * params are token, fromDate, toDate, event.  See
386
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
387
     * for more info
388
     */
389
    private void getLog()
390
    {
391
        OutputStream out = null;
392
        try
393
        {
394
            out = response.getOutputStream();
395
            AuthToken token = new AuthToken(sessionId);
396
            String fromDateS = params.get("fromDate")[0];
397
            Date fromDate = null;
398
            String toDateS = params.get("toDate")[0];
399
            Date toDate = null;
400
            String eventS = params.get("event")[0];
401
            Event event = null;
402 5391 berkley
            SimpleDateFormat sdf = new SimpleDateFormat();
403 5390 berkley
            if(fromDateS != null)
404
            {
405 5391 berkley
                fromDate = new Date(new Long(fromDateS).longValue());//sdf.parse(fromDateS, new ParsePosition(0));
406 5390 berkley
            }
407
            if(toDateS != null)
408
            {
409 5391 berkley
                toDate = new Date(new Long(toDateS).longValue());//sdf.parse(toDateS, new ParsePosition(0));
410 5390 berkley
            }
411
            if(eventS != null)
412
            {
413
                event = Event.convert(eventS);
414
            }
415 5395 berkley
416 5390 berkley
            Log log = CrudService.getInstance().getLogRecords(token, fromDate, toDate, event);
417
            serializeServiceType(Log.class, log, out);
418
        }
419
        catch(Exception e)
420
        {
421
            String msg = "Could not get logs from CrudService: " + e.getMessage();
422
            ServiceFailure sf = new ServiceFailure("1490", msg);
423
            logMetacat.error(msg);
424
            e.printStackTrace();
425
            serializeException(sf, out);
426
        }
427
    }
428
429
    /**
430 5374 berkley
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions
431
     */
432
    private void initParams() {
433 5211 jones
434 5374 berkley
        String name = null;
435
        String[] value = null;
436
        Enumeration paramlist = request.getParameterNames();
437
        while (paramlist.hasMoreElements()) {
438
            name = (String) paramlist.nextElement();
439
            value = request.getParameterValues(name);
440
            params.put(name, value);
441
        }
442
    }
443
444 5211 jones
    /**
445 5374 berkley
     *
446
     * Load user details of metacat session from the request
447
     *
448
     */
449
    private void loadSessionData()
450
      throws Exception
451
    {
452
        SessionData sessionData = RequestUtil.getSessionData(request);
453
        try
454
        {
455
            username = null;
456
            password = null;
457
            groupNames = null;
458
            sessionId = null;
459
460
            boolean validSession = false;
461
            SessionService ss = SessionService.getInstance();
462 5410 berkley
            System.out.println("sessionData: " + sessionData);
463
            System.out.println("username: " + sessionData.getUserName());
464
            System.out.println("sessionid: " + sessionData.getId());
465 5374 berkley
            //validate the session
466 5410 berkley
            if(ss.isSessionRegistered(sessionData.getId()) &&
467
               !(sessionData.getUserName().equals("public") || sessionData.getId().equals("0")))
468 5374 berkley
            {
469
                validSession = true;
470
            }
471
472
            if(validSession)
473
            {
474
                //if the session is valid, set these variables
475
                username = sessionData.getUserName();
476
                password = sessionData.getPassword();
477
                groupNames = sessionData.getGroupNames();
478
                sessionId = sessionData.getId();
479 5386 berkley
                System.out.println("setting sessionid to " + sessionId);
480
                System.out.println("username: " + username);
481 5374 berkley
            }
482
483
            //if the session is not valid or the username is null, set
484
            //username to public
485
            if (username == null)
486
            {
487 5386 berkley
                System.out.println("setting username to public.");
488 5374 berkley
                username = "public";
489
            }
490
        }
491
        catch(Exception e)
492
        {
493
            throw new Exception("Could not load the session data: " + e.getMessage());
494
        }
495
    }
496
497
    /**
498
     * generate missing system metadata for any science metadata objects
499
     * that don't already have it. https://trac.dataone.org/ticket/591
500 5381 berkley
     *
501
     * called with POST meta/?op=generatemissingsystemmetadata
502 5374 berkley
     */
503
    private void generateMissingSystemMetadata()
504
    {
505 5381 berkley
        AuthToken token = new AuthToken(sessionId);
506
        CrudService.getInstance().generateMissingSystemMetadata(token);
507 5374 berkley
    }
508
509
    /**
510
     *  Earthgrid API > Identifier Service > isRegistered Function :
511
     *  calls MetacatHandler > handleIdIsRegisteredAction
512 5286 jones
     * @param guid
513 5211 jones
     * @throws IOException
514
     */
515 5286 jones
    private void isRegistered(String guid) throws IOException
516
    {
517
518
        // Look up the localId for this guid
519
        IdentifierManager im = IdentifierManager.getInstance();
520
        String localId = "";
521
        try {
522
            localId = im.getLocalId(guid);
523
        } catch (McdbDocNotFoundException e) {
524
            // TODO: Need to return the proper DataONE exception
525
        }
526
527
        params.put("docid", new String[] { localId });
528 5211 jones
        PrintWriter out = response.getWriter();
529
        handler.handleIdIsRegisteredAction(out, params, response);
530
        out.close();
531
    }
532
533
    /**
534 5374 berkley
     * Earthgrid API > Identifier Service > getAllDocIds Function :
535
     * calls MetacatHandler > handleGetAllDocidsAction
536 5211 jones
     * @throws IOException
537
     */
538
    private void getAllDocIds() throws IOException {
539
        PrintWriter out = response.getWriter();
540
        handler.handleGetAllDocidsAction(out, params, response);
541
        out.close();
542
    }
543
544
    /**
545 5374 berkley
     * Earthgrid API > Identifier Service > getNextRevision Function :
546
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
547 5286 jones
     * @param guid
548 5211 jones
     * @throws IOException
549
     */
550 5286 jones
    private void getNextRevision(String guid) throws IOException
551
    {
552
        params.put("docid", new String[] { guid });
553 5211 jones
        PrintWriter out = response.getWriter();
554
        //handler.handleGetRevisionAndDocTypeAction(out, params);
555
556
        try {
557
            // Make sure there is a docid
558 5286 jones
            if (guid == null || guid.equals("")) {
559 5211 jones
                throw new Exception("User didn't specify docid!");
560 5286 jones
            }
561 5211 jones
562 5286 jones
            // Look up the localId for this guid
563
            IdentifierManager im = IdentifierManager.getInstance();
564
            String localId = "";
565
            try {
566
                localId = im.getLocalId(guid);
567
            } catch (McdbDocNotFoundException e) {
568
                // TODO: Need to return the proper DataONE exception
569
            }
570
571 5211 jones
            // Create a DBUtil object
572
            DBUtil dbutil = new DBUtil();
573
            // Get a rev and doctype
574
            String revAndDocType = dbutil
575 5286 jones
                    .getCurrentRevisionAndDocTypeForGivenDocument(localId);
576 5211 jones
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
577
578
            out.println("<?xml version=\"1.0\"?>");
579
            out.print("<next-revision>");
580
            out.print(revision);
581
            out.print("</next-revision>");
582
583
        } catch (Exception e) {
584
            // Handle exception
585
            out.println("<?xml version=\"1.0\"?>");
586
            out.println("<error>");
587
            out.println(e.getMessage());
588
            out.println("</error>");
589
        }
590
591
        out.close();
592
    }
593
594
    /**
595 5374 berkley
     * Earthgrid API > Identifier Service > getNextObject Function :
596
     * calls MetacatHandler > handleGetMaxDocidAction
597 5211 jones
     * @throws IOException
598
     */
599
    private void getNextObject() throws IOException {
600
        PrintWriter out = response.getWriter();
601
        handler.handleGetMaxDocidAction(out, params, response);
602
        out.close();
603
    }
604
605
    /**
606 5299 jones
     * Implements REST version of DataONE CRUD API --> get
607 5282 jones
     * @param guid ID of data object to be read
608 5211 jones
     */
609 5282 jones
    private void getObject(String guid) {
610 5337 berkley
        CrudService cs = CrudService.getInstance();
611
        cs.setParamsFromRequest(request);
612 5370 berkley
        AuthToken token = new AuthToken(sessionId);
613 5319 jones
        OutputStream out = null;
614 5282 jones
        try {
615 5319 jones
            out = response.getOutputStream();
616 5370 berkley
            if(guid != null)
617
            { //get a specific document
618
                Identifier id = new Identifier();
619
                id.setValue(guid);
620
                InputStream data = cs.get(token, id);
621
                IOUtils.copyLarge(data, response.getOutputStream());
622
            }
623
            else
624
            { //call listObjects with specified params
625
                Date startTime = null;
626
                Date endTime = null;
627
                ObjectFormat objectFormat = null;
628
                boolean replicaStatus = false;
629
                int start = 0;
630
                int count = 1000;
631
                Enumeration paramlist = request.getParameterNames();
632
                while (paramlist.hasMoreElements())
633
                { //parse the params and make the crud call
634
                    String name = (String) paramlist.nextElement();
635
                    String[] value = (String[])request.getParameterValues(name);
636 5391 berkley
                    System.out.println("name: " + name + " value: " + value);
637
                    if(name.equals("startTime") && value != null)
638 5370 berkley
                    {
639
                        try
640
                        {
641
                          startTime = DateFormat.getDateTimeInstance().parse(value[0]);
642
                        }
643
                        catch(Exception e)
644
                        {  //if we can't parse it, just don't use the startTime param
645
                            System.out.println("Could not parse startTime: " + value[0]);
646
                            startTime = null;
647
                        }
648
                    }
649 5391 berkley
                    else if(name.equals("endTime") && value != null)
650 5370 berkley
                    {
651
                        try
652
                        {
653
                          endTime = DateFormat.getDateTimeInstance().parse(value[0]);
654
                        }
655
                        catch(Exception e)
656
                        {  //if we can't parse it, just don't use the endTime param
657
                            System.out.println("Could not parse endTime: " + value[0]);
658
                            endTime = null;
659
                        }
660
                    }
661 5391 berkley
                    else if(name.equals("objectFormat") && value != null)
662 5370 berkley
                    {
663
                        objectFormat = ObjectFormat.convert(value[0]);
664
                    }
665 5391 berkley
                    else if(name.equals("replicaStatus") && value != null)
666 5370 berkley
                    {
667 5391 berkley
                        if(value != null &&
668
                           value.length > 0 &&
669
                           (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
670 5370 berkley
                        {
671
                            replicaStatus = true;
672
                        }
673
                    }
674 5391 berkley
                    else if(name.equals("start") && value != null)
675 5370 berkley
                    {
676
                        start = new Integer(value[0]).intValue();
677
                    }
678 5391 berkley
                    else if(name.equals("count") && value != null)
679 5370 berkley
                    {
680
                        count = new Integer(value[0]).intValue();
681
                    }
682
                }
683
                //make the crud call
684 5410 berkley
                /*System.out.println("token: " + token + " startTime: " + startTime +
685 5370 berkley
                        " endtime: " + endTime + " objectFormat: " +
686
                        objectFormat + " replicaStatus: " + replicaStatus +
687
                        " start: " + start + " count: " + count);
688 5410 berkley
                */
689 5370 berkley
                ObjectList ol = cs.listObjects(token, startTime, endTime,
690
                        objectFormat, replicaStatus, start, count);
691 5410 berkley
                ol = cs.listObjects(token, null, null, null, false, 0, 1000);
692 5370 berkley
693 5395 berkley
                StringReader sr = new StringReader(ol.toString());
694 5370 berkley
                out = response.getOutputStream();
695
                // Serialize and write it to the output stream
696
                try {
697 5390 berkley
                    serializeServiceType(ObjectList.class, ol, out);
698 5370 berkley
                } catch (JiBXException e) {
699
                    throw new ServiceFailure("1190", "Failed to serialize ObjectList: " + e.getMessage());
700
                }
701
            }
702 5299 jones
        } catch (BaseException e) {
703 5319 jones
                serializeException(e, out);
704 5299 jones
        } catch (IOException e) {
705 5356 berkley
            ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
706 5319 jones
            serializeException(sf, out);
707 5282 jones
        }
708 5211 jones
    }
709
710
    /**
711 5332 jones
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
712
     * @param guid ID of data object to be read
713
     */
714
    private void getSystemMetadataObject(String guid) {
715 5337 berkley
        CrudService cs = CrudService.getInstance();
716
        cs.setParamsFromRequest(request);
717 5386 berkley
        AuthToken token = new AuthToken(sessionId);
718 5332 jones
        OutputStream out = null;
719
        try {
720
            out = response.getOutputStream();
721
            Identifier id = new Identifier();
722
            id.setValue(guid);
723
            SystemMetadata sysmeta = cs.getSystemMetadata(token, id);
724
725
            // Serialize and write it to the output stream
726
            try {
727 5390 berkley
                serializeServiceType(SystemMetadata.class, sysmeta, out);
728 5332 jones
            } catch (JiBXException e) {
729 5356 berkley
                throw new ServiceFailure("1190", "Failed to serialize SystemMetadata: " + e.getMessage());
730 5332 jones
            }
731
        } catch (BaseException e) {
732
                serializeException(e, out);
733
        } catch (IOException e) {
734 5356 berkley
            ServiceFailure sf = new ServiceFailure("1030", e.getMessage());
735 5332 jones
            serializeException(sf, out);
736
        } finally {
737
            IOUtils.closeQuietly(out);
738
        }
739
    }
740
741
    /**
742 5390 berkley
     * serialize an object of type to out
743
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
744
     * @param object the object to serialize
745
     * @param out the stream to serialize it to
746
     * @throws JiBXException
747
     */
748
    private void serializeServiceType(Class type, Object object, OutputStream out)
749
      throws JiBXException
750
    {
751
        IBindingFactory bfact = BindingDirectory.getFactory(type);
752
        IMarshallingContext mctx = bfact.createMarshallingContext();
753
        mctx.marshalDocument(object, "UTF-8", null, out);
754
    }
755
756
    /**
757
     * deserialize an object of type from is
758
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
759
     * @param is the stream to deserialize from
760
     * @throws JiBXException
761
     */
762
    private Object deserializeServiceType(Class type, InputStream is)
763
      throws JiBXException
764
    {
765
        IBindingFactory bfact = BindingDirectory.getFactory(type);
766
        IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
767
        Object o = (Object) uctx.unmarshalDocument(is, null);
768
        return o;
769
    }
770
771
    /**
772 5211 jones
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query
773
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
774
     *
775
     * NOTE:
776
     *      This is the only method that uses EcoGrid classes for its implementation.
777
     *      It does so because it takes an EcoGrid Query as input, and outputs an
778
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
779
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
780
     *      This creates a circular dependency, because the Metacat classes are needed
781
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
782
     *      method.  This circularity could be resolved by moving the EcoGrid classes
783
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
784
     *      favor of these REST interfaces, this circular dependency can be eliminated.
785
     *
786
     * @throws Exception
787
     */
788
    private void query() throws Exception {
789
        /*  This block commented out because of the EcoGrid circular dependency.
790
         *  For now, query will not be supported until the circularity can be
791 5286 jones
         *  resolved, probably by moving the ecogrid query syntax transformers
792 5211 jones
         *  directly into the Metacat codebase.  MBJ 2010-02-03
793
794
        try {
795
            EcogridQueryParser parser = new EcogridQueryParser(request
796
                    .getReader());
797
            parser.parseXML();
798
            QueryType queryType = parser.getEcogridQuery();
799
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer =
800
                new EcogridJavaToMetacatJavaQueryTransformer();
801
            QuerySpecification metacatQuery = queryTransformer
802
                    .transform(queryType);
803
804
            DBQuery metacat = new DBQuery();
805
806
            boolean useXMLIndex = (new Boolean(PropertyService
807
                    .getProperty("database.usexmlindex"))).booleanValue();
808
            String xmlquery = "query"; // we don't care the query in resultset,
809
            // the query can be anything
810
            PrintWriter out = null; // we don't want metacat result, so set out null
811
812
            // parameter: queryspecification, user, group, usingIndexOrNot
813
            StringBuffer result = metacat.createResultDocument(xmlquery,
814
                    metacatQuery, out, username, groupNames, useXMLIndex);
815
816
            // create result set transfer
817
            String saxparser = PropertyService.getProperty("xml.saxparser");
818
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
819
                    new StringReader(result.toString()), saxparser, queryType
820
                            .getNamespace().get_value());
821
            ResultsetType records = metacatResultsetParser.getEcogridResult();
822
823
            System.out
824
                    .println(EcogridResultsetTransformer.toXMLString(records));
825
            response.setContentType("text/xml");
826
            out = response.getWriter();
827
            out.print(EcogridResultsetTransformer.toXMLString(records));
828
829
        } catch (Exception e) {
830
            e.printStackTrace();
831
        }*/
832
        response.setContentType("text/xml");
833
        PrintWriter out = response.getWriter();
834
        out.print("<error>Query operation not yet supported by Metacat.</error>");
835
        out.close();
836
    }
837 5319 jones
838 5394 berkley
    private String streamToString(InputStream is)
839
    throws IOException
840
    {
841
        byte b[] = new byte[1024];
842
        int numread = is.read(b, 0, 1024);
843
        String response = new String();
844
        while(numread != -1)
845
        {
846
            response += new String(b, 0, numread);
847
            numread = is.read(b, 0, 1024);
848
        }
849
        return response;
850
    }
851
852
    private InputStream stringToStream(String s)
853
    throws IOException
854
    {
855
        ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
856
        return bais;
857
    }
858
859
860 5211 jones
    /**
861
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction
862
     *
863 5319 jones
     * @param guid ID of data object to be inserted or updated
864 5211 jones
     * @throws IOException
865
     */
866 5319 jones
    private void putObject(String guid, String action) {
867
        logMetacat.debug("Entering putObject: " + guid + "/" + action);
868
        OutputStream out = null;
869
        try {
870
            out = response.getOutputStream();
871
        } catch (IOException e1) {
872
            logMetacat.error("Could not get the output stream for writing in putObject");
873
        }
874
        try {
875 5353 berkley
876
            // Read the incoming data from its Mime Multipart encoding
877
            logMetacat.debug("Disassembling MIME multipart form");
878
            InputStream object = null;
879
            InputStream sysmeta = null;
880 5394 berkley
881 5395 berkley
            //HACK: Since mmp seems to have a bug where large object parts get truncated,
882
            //parse the stream here.  This has the disavantage of putting the
883
            //stream into memory.
884 5394 berkley
            String s = streamToString(request.getInputStream());
885
            String searchString = "Content-Disposition: attachment; filename=systemmetadata";
886
            String endString = "------=_Part";
887
            int searchStringIndex = s.indexOf(searchString);
888
            sysmeta = new ByteArrayInputStream(
889
                    s.substring(searchStringIndex +
890
                            searchString.length(),
891
                            s.indexOf(endString, searchStringIndex)).trim().getBytes());
892
893
            searchString = "Content-Disposition: attachment; filename=object";
894
            searchStringIndex = s.indexOf(searchString);
895
            object = new ByteArrayInputStream(
896
                    s.substring(searchStringIndex +
897
                            searchString.length(),
898
                            s.indexOf(endString, searchStringIndex)).trim().getBytes());
899
900
            /*
901 5395 berkley
            //This code should work, however there seems to be a bug in part.getInputStream()
902
            //where it does not return the entire stream.  Only about the first 6kB
903
            //are actually returned.
904 5353 berkley
            MimeMultipart mmp = new MimeMultipart(new InputStreamDataSource("message", request.getInputStream()));
905
            logMetacat.debug("MMP created.");
906 5394 berkley
907
            //mmp.writeTo(System.out);
908 5353 berkley
            for (int i = 0; i < mmp.getCount(); i++) {
909
                BodyPart part = mmp.getBodyPart(i);
910
                String name = part.getFileName();
911
                logMetacat.debug("Part name is: " + name);
912
                logMetacat.debug("Part has class name: " + part.getClass().getName());
913 5394 berkley
914 5353 berkley
                if (name.equals("object")) {
915
                    object = part.getInputStream();
916 5394 berkley
                    try
917
                    {
918
                        String s = streamToString(object);
919
                        System.out.println("doc text: " + s);
920
                        System.out.println("size of doc text is: " + s.getBytes().length);
921
                        object = stringToStream(s);
922
                    }
923
                    catch(Exception e)
924
                    {}
925
                    System.out.println("Found object part, size is: " + part.getSize());
926 5353 berkley
                } else if (name.equals("systemmetadata")) {
927
                    sysmeta = part.getInputStream();
928 5394 berkley
                    try
929
                    {
930
                      String s = streamToString(sysmeta);
931
                      System.out.println("system metadata part: " + s);
932
                      sysmeta = stringToStream(s);
933
                    }
934
                    catch(Exception e){}
935
                    System.out.println("system metadata part, size is " + part.getSize());
936 5353 berkley
                    logMetacat.debug("Found sysmeta part, size is: " + part.getSize());
937
                } else {
938 5356 berkley
                    throw new InvalidRequest("1000", "Request had malformed MIME part with name: " + name);
939 5353 berkley
                }
940 5394 berkley
            }*/
941 5353 berkley
942
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
943 5211 jones
944 5319 jones
                // Check if the objectId exists
945
                IdentifierManager im = IdentifierManager.getInstance();
946
                if (im.identifierExists(guid)) {
947 5356 berkley
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
948 5319 jones
                }
949 5211 jones
950 5376 berkley
                logMetacat.debug("Commence creation...");
951
                IBindingFactory bfact =
952
                    BindingDirectory.getFactory(SystemMetadata.class);
953
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
954
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
955
956
                CrudService cs = CrudService.getInstance();
957 5386 berkley
                AuthToken token = new AuthToken(sessionId);
958 5376 berkley
                cs.setParamsFromRequest(request);
959
                Identifier id = new Identifier();
960
                id.setValue(guid);
961
                cs.create(token, id, object, m);
962 5386 berkley
963 5353 berkley
            } else if (action.equals(FUNCTION_NAME_UPDATE)) { //handle updates
964
                IdentifierManager im = IdentifierManager.getInstance();
965
                CrudService cs = CrudService.getInstance();
966
                Identifier obsoletedGuid = new Identifier();
967
                Identifier id = new Identifier();
968
                id.setValue(guid);
969 5386 berkley
                AuthToken token = new AuthToken(sessionId);
970 5353 berkley
971
                //do some checks
972
                if(params.get("obsoletedGuid") == null)
973
                {
974 5356 berkley
                    throw new InvalidRequest("1202", "obsoletedGuid must be contained in the request parameters.");
975 5353 berkley
                }
976
                //get the obsoletedGuid
977
                String[] obsGuidS = params.get("obsoletedGuid");
978
                obsoletedGuid.setValue(obsGuidS[0]);
979
980
                if (!im.identifierExists(obsoletedGuid.getValue()))
981
                {
982 5356 berkley
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
983 5353 berkley
                }
984
985
986
                logMetacat.debug("Commence update...");
987
988
                //get the systemmetadata
989
                IBindingFactory bfact =
990
                        BindingDirectory.getFactory(SystemMetadata.class);
991
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
992
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
993
994
                //do the update
995
                try
996
                {
997
                    cs.setParamsFromRequest(request);
998
                    Identifier rId = cs.update(token, id, object, obsoletedGuid, m);
999
                }
1000
                catch(NotFound e)
1001
                {
1002 5356 berkley
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1003 5353 berkley
                }
1004
1005 5211 jones
            } else {
1006 5356 berkley
                throw new InvalidRequest("1000", "Operation must be create or update.");
1007 5211 jones
            }
1008 5319 jones
        } catch (NotAuthorized e) {
1009
            serializeException(e, out);
1010
        } catch (InvalidToken e) {
1011
            serializeException(e, out);
1012
        } catch (ServiceFailure e) {
1013
            serializeException(e, out);
1014
        } catch (IdentifierNotUnique e) {
1015
            serializeException(e, out);
1016
        } catch (UnsupportedType e) {
1017
            serializeException(e, out);
1018
        } catch (InsufficientResources e) {
1019
            serializeException(e, out);
1020
        } catch (InvalidSystemMetadata e) {
1021
            serializeException(e, out);
1022
        } catch (NotImplemented e) {
1023
            serializeException(e, out);
1024
        } catch (InvalidRequest e) {
1025
            serializeException(e, out);
1026 5394 berkley
        } /*catch (MessagingException e) {
1027 5356 berkley
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1028 5319 jones
            serializeException(sf, out);
1029 5394 berkley
        }*/ catch (IOException e) {
1030 5356 berkley
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1031 5319 jones
            serializeException(sf, out);
1032 5320 jones
        } catch (JiBXException e) {
1033
            e.printStackTrace(System.out);
1034 5356 berkley
            InvalidSystemMetadata ism = new InvalidSystemMetadata("1080", e.getMessage());
1035 5320 jones
            serializeException(ism, out);
1036 5211 jones
        }
1037
    }
1038
1039
    /**
1040
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction
1041
     *
1042 5287 jones
     * @param guid ID of data object to be deleted
1043 5211 jones
     * @throws IOException
1044
     */
1045 5287 jones
    private void deleteObject(String guid) throws IOException
1046
    {
1047
        // Look up the localId for this global identifier
1048
        IdentifierManager im = IdentifierManager.getInstance();
1049
        String localId = "";
1050
        try {
1051
            localId = im.getLocalId(guid);
1052
        } catch (McdbDocNotFoundException e) {
1053
            // TODO: Need to return the proper DataONE exception
1054
        }
1055
1056
        params.put("docid", new String[] { localId });
1057 5211 jones
        PrintWriter out = response.getWriter();
1058
        handler.handleDeleteAction(out, params, request, response, username,
1059
                groupNames);
1060
        out.close();
1061
    }
1062 5370 berkley
1063
    /**
1064
     * set the access perms on a document
1065
     * @throws IOException
1066
     */
1067
    private void setaccess() throws IOException
1068
    {
1069
        try
1070
        {
1071
            String guid = params.get("guid")[0];
1072
            Identifier id = new Identifier();
1073
            id.setValue(guid);
1074
            AuthToken token = new AuthToken(sessionId);
1075
            String principal = params.get("principal")[0];
1076
            String permission = params.get("permission")[0];
1077
            String permissionType = params.get("permissionType")[0];
1078
            String permissionOrder = params.get("permissionOrder")[0];
1079
            String setSystemMetadata = params.get("setsystemmetadata")[0];
1080
            CrudService cs = CrudService.getInstance();
1081
            cs.setAccess(token, id, principal, permission, permissionType, permissionOrder);
1082
            if(setSystemMetadata.equals("true") || setSystemMetadata.equals("TRUE") ||
1083
               setSystemMetadata.equals("yes"))
1084
            { //set the same perms on the system metadata doc
1085
                IdentifierManager im = IdentifierManager.getInstance();
1086
                String smidS = im.getSystemMetadataId(id.getValue());
1087
                Identifier smid = new Identifier();
1088
                smid.setValue(smidS);
1089 5411 berkley
                cs.setAccess(token, smid, principal, permission, permissionType, permissionOrder);
1090 5370 berkley
            }
1091
        }
1092
        catch(Exception e)
1093
        {
1094
            printError("Error setting access in ResourceHandler: " + e.getMessage(), response);
1095
        }
1096
    }
1097 5211 jones
1098
    /**
1099
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
1100
     *
1101
     * @throws IOException
1102
     */
1103
    private void login() throws IOException {
1104
        PrintWriter out = response.getWriter();
1105
        handler.handleLoginAction(out, params, request, response);
1106
        out.close();
1107
    }
1108
1109
    /**
1110
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
1111
     *
1112
     * @throws IOException
1113
     */
1114
    private void logout() throws IOException {
1115
        PrintWriter out = response.getWriter();
1116
        handler.handleLogoutAction(out, params, request, response);
1117
        out.close();
1118
    }
1119
1120
    /**
1121
     * Prints xml response
1122
     * @param message Message to be displayed
1123
     * @param response Servlet response that xml message will be printed
1124
     * */
1125
    private void printError(String message, HttpServletResponse response) {
1126
        try {
1127
            PrintWriter out = response.getWriter();
1128
            response.setContentType("text/xml");
1129
            out.println("<?xml version=\"1.0\"?>");
1130
            out.println("<error>");
1131
            out.println(message);
1132
            out.println("</error>");
1133
            out.close();
1134
        } catch (IOException e) {
1135
            e.printStackTrace();
1136
        }
1137
    }
1138 5370 berkley
1139 5319 jones
    private void serializeException(BaseException e, OutputStream out) {
1140
        // TODO: Use content negotiation to determine which return format to use
1141
        response.setContentType("text/xml");
1142
        response.setStatus(e.getCode());
1143
        try {
1144
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
1145
        } catch (IOException e1) {
1146
            logMetacat.error("Error writing exception to stream. "
1147
                    + e1.getMessage());
1148
        }
1149
    }
1150
1151 5211 jones
}