Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: Serhan AKIN $'
7
 *     '$Date: 2009-06-13 15:28:13 +0300  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.restservice;
24

    
25
import java.io.*;
26
import java.util.*;
27

    
28
import javax.mail.BodyPart;
29
import javax.mail.MessagingException;
30
import javax.mail.internet.MimeMultipart;
31
import javax.servlet.ServletContext;
32
import javax.servlet.http.HttpServletRequest;
33
import javax.servlet.http.HttpServletResponse;
34
import java.text.DateFormat;
35
import java.text.ParseException;
36
import java.text.ParsePosition;
37
import java.text.SimpleDateFormat;
38

    
39

    
40
import org.apache.commons.httpclient.util.DateParser;
41
import org.apache.commons.io.IOUtils;
42
import org.apache.log4j.Logger;
43
import org.apache.maven.artifact.ant.shaded.IOUtil;
44
import org.dataone.service.exceptions.BaseException;
45
import org.dataone.service.exceptions.IdentifierNotUnique;
46
import org.dataone.service.exceptions.InsufficientResources;
47
import org.dataone.service.exceptions.InvalidRequest;
48
import org.dataone.service.exceptions.InvalidSystemMetadata;
49
import org.dataone.service.exceptions.InvalidToken;
50
import org.dataone.service.exceptions.NotAuthorized;
51
import org.dataone.service.exceptions.NotImplemented;
52
import org.dataone.service.exceptions.ServiceFailure;
53
import org.dataone.service.exceptions.UnsupportedType;
54
import org.dataone.service.exceptions.NotFound;
55
import org.dataone.service.types.*;
56
import org.jibx.runtime.BindingDirectory;
57
import org.jibx.runtime.IBindingFactory;
58
import org.jibx.runtime.IMarshallingContext;
59
import org.jibx.runtime.IUnmarshallingContext;
60
import org.jibx.runtime.JiBXException;
61

    
62
import edu.ucsb.nceas.metacat.DBUtil;
63
import edu.ucsb.nceas.metacat.IdentifierManager;
64
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
65
import edu.ucsb.nceas.metacat.MetacatHandler;
66
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
67
import edu.ucsb.nceas.metacat.dataone.CrudService;
68
import edu.ucsb.nceas.metacat.properties.PropertyService;
69
import edu.ucsb.nceas.metacat.service.SessionService;
70
import edu.ucsb.nceas.metacat.util.RequestUtil;
71
import edu.ucsb.nceas.metacat.util.SessionData;
72
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
73

    
74
import org.dataone.service.streaming.util.StreamUtil;
75

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

    
150
 * </ul>
151
 * </li>
152
 * 
153
 * <li>
154
 * <h3>EarthGrid Identifier Service</h3><br/>
155
 * 
156
 * <ul>
157
 * <li><h3>isRegistered: </h3>      <br/>
158
 * <b>REST URL:</b> <code>GET, [context-root]/identifier/[doc-id]?op=isregistered</code>   <br/>
159
 * <b>Returns:</b> message in XML format<br/><br/>
160
 * </li>
161

    
162
 * <li><h3>getAllDocIds:</h3>       <br/>       
163
 * <b>REST URL:</b> <code>GET, [context-root]/identifier?op=getalldocids</code>   <br/>
164
 * <b>Returns:</b> document id list in XML format<br/><br/>
165
 * </li>
166
 * 
167
 * <li><h3>addLSID Function:</h3> 
168
 * Metacat does not support this function       <br/>
169
 * <b>REST URL:</b> <code>PUT, [context-root]/identifier/[doc-id]</code>   <br/>
170
 * <b>Returns:</b> error message in XML format<br/><br/>
171
 * </li>
172
 * 
173
 * <li><h3>getNextRevision:</h3>        <br/>
174
 * <b>REST URL:</b> <code>GET, [context-root]/identifier/[doc-id]?op=getnextrevision</code>   <br/>
175
 * <b>Returns:</b> message in XML format<br/><br/>
176
 * </li>
177
 * 
178
 * <li><h3>getNextObject:</h3>      <br/>
179
 * <b>REST URL:</b> <code>GET, [context-root]/identifier?op=getnextobject&scope=[scope]</code>   <br/>
180
 * <b>Returns:</b> message in XML format<br/><br/>
181
 * </li>
182
 * 
183
 * </li>
184
 * </ul>
185
 * 
186
 */
187
public class ResourceHandler {
188

    
189
    /**HTTP Verb GET*/
190
    public static final byte GET = 1;
191
    /**HTTP Verb POST*/
192
    public static final byte POST = 2;
193
    /**HTTP Verb PUT*/
194
    public static final byte PUT = 3;
195
    /**HTTP Verb DELETE*/
196
    public static final byte DELETE = 4;
197
    /**HTTP Verb HEAD*/
198
    public static final byte HEAD = 5;
199

    
200
    /*
201
     * API Resources
202
     */
203
    private static final String RESOURCE_OBJECTS = "object";
204
    private static final String RESOURCE_META = "meta";
205
    private static final String RESOURCE_SESSION = "session";
206
    private static final String RESOURCE_IDENTIFIER = "identifier";
207
    private static final String RESOURCE_LOG = "log";
208
    private static final String RESOURCE_CHECKSUM = "checksum";
209

    
210
    /*
211
     * API Functions used as URL parameters
212
     */
213
    private static final String FUNCTION_KEYWORD = "op";
214
    private static final String FUNCTION_NAME_LOGIN = "login";
215
    private static final String FUNCTION_NAME_LOGOUT = "logout";
216
    private static final String FUNCTION_NAME_SET_ACCESS = "setaccess";
217
    private static final String FUNCTION_NAME_ISREGISTERED = "isregistered";
218
    private static final String FUNCTION_NAME_GETALLDOCS = "getalldocids";
219
    private static final String FUNCTION_NAME_GETNEXTREV = "getnextrevision";
220
    private static final String FUNCTION_NAME_GETNEXTOBJ = "getnextobject";
221
    private static final String FUNCTION_NAME_INSERT = "insert";
222
    private static final String FUNCTION_NAME_UPDATE = "update";
223
    private static final String FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA = "generatemissingsystemmetadata";
224

    
225
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
226
    
227
    private ServletContext servletContext;
228
    private Logger logMetacat;
229
    private MetacatHandler handler;
230
    private HttpServletRequest request;
231
    private HttpServletResponse response;
232
    private String username;
233
    private String password;
234
    private String sessionId;
235
    private String[] groupNames;
236

    
237
    private Hashtable<String, String[]> params;
238

    
239
    /**Initializes new instance by setting servlet context,request and response*/
240
    public ResourceHandler(ServletContext servletContext,
241
            HttpServletRequest request, HttpServletResponse response) {
242
        this.servletContext = servletContext;
243
        this.request = request;
244
        this.response = response;
245
    }
246

    
247
    /**
248
     * This function is called from REST APU servlet and handles each request to the servlet 
249
     * 
250
     * @param httpVerb (GET, POST, PUT or DELETE)
251
     */
252
    public void handle(byte httpVerb) {
253
        logMetacat = Logger.getLogger(ResourceHandler.class);
254
        try {
255
            String resource = request.getServletPath();
256
            String verb = "";
257
            
258
            System.out.println("handling verb " + httpVerb + " request with resource " + resource);
259
            boolean status = false;
260
            loadSessionData();
261

    
262
            if (resource != null) {
263
                resource = request.getServletPath().substring(1);
264

    
265
                params = new Hashtable<String, String[]>();
266
                initParams();
267

    
268
                Timer timer = new Timer();
269
                handler = new MetacatHandler(timer);
270

    
271
                if (resource.equals(RESOURCE_SESSION) && 
272
                        httpVerb == POST && 
273
                        params.get(FUNCTION_KEYWORD) != null) {
274
                    //System.out.println("function_keyword: " + params.get(FUNCTION_KEYWORD)[0]);
275
                    if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGIN)) {
276
                        login();
277
                        status = true;
278
                    } else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGOUT)) {
279
                        logout();
280
                        status = true;
281
                    } else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_SET_ACCESS)) {
282
                        setaccess();
283
                        status = true;
284
                        //System.out.println("done setting access");
285
                    }
286
                } else if (resource.equals(RESOURCE_META)) {
287
                    if(params != null && params.get(FUNCTION_KEYWORD) != null &&
288
                            params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA))
289
                    { //generate system metadata for any object that is
290
                        //a) not system metadata itself
291
                        //b) does not already have a system metadata id in the systemmetadata table
292
                        //c) not a BIN object (data)
293
                        //TODO: check if we need this anymore.  Might be superceded
294
                        //by MetacatPopulator
295
                        generateMissingSystemMetadata();
296
                        status = true;
297
                    }
298
                    else
299
                    {
300
                        String objectId = request.getPathInfo();
301
                        if (objectId != null && objectId.length() > 1) 
302
                        {
303
                            objectId = request.getPathInfo().substring(1);
304
                        }
305
                        getSystemMetadataObject(objectId);
306
                        status = true;
307
                    }
308

    
309
                } else if (resource.equals(RESOURCE_OBJECTS)) {
310
                    logMetacat.debug("D1 Rest: Starting resource processing...");
311
                    loadSessionData();
312

    
313
                    String objectId = request.getPathInfo();
314
                    if (objectId != null && objectId.length() > 1) 
315
                    {
316
                        objectId = request.getPathInfo().substring(1);
317
                    }
318
                    else
319
                    {
320
                        objectId = null;
321
                    }
322

    
323
                    logMetacat.debug("verb:" + httpVerb);
324

    
325
                    if (httpVerb == GET) {
326
                        getObject(objectId);
327
                        status = true;
328
                    } else if (httpVerb == POST) {
329
                        putObject(objectId, FUNCTION_NAME_INSERT);
330
                        status = true;
331
                    } else if (httpVerb == PUT) {
332
                        putObject(objectId, FUNCTION_NAME_UPDATE);
333
                        status = true;
334
                    } else if (httpVerb == DELETE) {
335
                        deleteObject(objectId);
336
                        status = true;
337
                    } else if (httpVerb == HEAD) {
338
                        describeObject(objectId);
339
                        status = true;
340
                    }
341
                    
342

    
343
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
344

    
345
                    String identifierId = request.getPathInfo();
346
                    if (identifierId != null && identifierId.length() > 1)
347
                        identifierId = request.getPathInfo().substring(1); //trim the slash
348

    
349
                    if (httpVerb == GET) {
350
                        String op = params.get(FUNCTION_KEYWORD)[0];
351
                        if (op.equals(FUNCTION_NAME_ISREGISTERED)) {
352
                            isRegistered(identifierId);
353
                            status = true;
354
                        } else if (op.equals(FUNCTION_NAME_GETALLDOCS)) {
355
                            getAllDocIds();
356
                            status = true;
357
                        } else if (op.equals(FUNCTION_NAME_GETNEXTREV)) {
358
                            getNextRevision(identifierId);
359
                            status = true;
360
                        } else if (op.equals(FUNCTION_NAME_GETNEXTOBJ)) {
361
                            getNextObject();
362
                            status = true;
363
                        } 
364

    
365
                    } else if (httpVerb == PUT) {
366
                        //Earthgrid API > Identifier Service > addLSID Function 
367
                        response.setStatus(501);
368
                        printError(
369
                                "This method is not supported by metacat.  To "
370
                                + "add a new LSID, add a document to metacat.",
371
                                response);
372
                        status = true;
373
                    }
374

    
375
                } else if (resource.equals(RESOURCE_LOG)) {
376
                    //handle log events
377
                    if(httpVerb == GET)
378
                    {
379
                        getLog();
380
                        status = true;
381
                    }
382
                    else
383
                    {
384
                        //change to D1 spec for specifying which http methods are allowed for a resource
385
                        response.setStatus(501);
386
                        printError("POST, PUT, DELETE is not supported for logs.", response);
387
                        status = true;
388
                    }
389

    
390
                } else if(resource.equals(RESOURCE_CHECKSUM)) {
391
                    //handle checksum requests
392
                    System.out.println("Handling getChecksum request");
393
                    if(httpVerb == GET)
394
                    {
395
                        String guid = null;
396
                        String checksumAlgorithm = "MD5";
397
                    
398
                        try
399
                        {
400
                           guid = params.get("id")[0];
401
                        }
402
                        catch(Exception e)
403
                        {
404
                            throw new InvalidRequest("1402", "Incorrect parameters passed to getChecksum");
405
                        }
406
                        
407
                        Identifier guidid = new Identifier();
408
                        guidid.setValue(guid);
409
                        AuthToken token = new AuthToken(sessionId);
410
                        try
411
                        {
412
                            checksumAlgorithm = params.get("checksumAlgorithm")[0];
413
                        }
414
                        catch(Exception e)
415
                        {
416
                            //do nothing.  default to MD5
417
                        }
418
                        System.out.println("getting checksum for object " + guid +
419
                                " with algorithm " + checksumAlgorithm);
420
                        try
421
                        {
422
                            Checksum c = CrudService.getInstance().getChecksum(token, guidid, checksumAlgorithm);
423
                            System.out.println("got checksum " + c.getValue());
424
                            response.setStatus(200);
425
                            System.out.println("serializing response");
426
                            serializeServiceType(Checksum.class, c, response.getOutputStream());
427
                            System.out.println("done serializing response.");
428
                        }
429
                        catch(NotAuthorized na)
430
                        {
431
                            na.setDetail_code("1400");
432
                            serializeException(na, response.getOutputStream());
433
                        }
434
                        catch(NotFound nf)
435
                        {
436
                            nf.setDetail_code("1420");
437
                            serializeException(nf, response.getOutputStream());
438
                        }
439
                        catch(InvalidRequest ir)
440
                        {
441
                            ir.setDetail_code("1402");
442
                            serializeException(ir, response.getOutputStream());
443
                        }
444
                        catch(ServiceFailure sf)
445
                        {
446
                            sf.setDetail_code("1410");
447
                            serializeException(sf, response.getOutputStream());
448
                        }
449
                        catch(InvalidToken it)
450
                        {
451
                            it.setDetail_code("1430");
452
                            serializeException(it, response.getOutputStream());
453
                        }
454
                        status = true;
455
                    }
456
                }
457
                    
458
                if (!status)
459
                {
460
                    response.setStatus(400);
461
                    printError("Incorrect parameters!", response);
462
                }
463
            } else {
464
                response.setStatus(400);
465
                printError("Incorrect resource!", response);
466
            }
467
        } catch (Exception e) {
468
            logMetacat.error(e.getMessage());
469
            e.printStackTrace();
470
        }
471
    }
472
    
473
    /**
474
     * MN_crud.describe()
475
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
476
     * @param guid
477
     */
478
    private void describeObject(String guid)
479
    {
480
        Logger logMetacat = Logger.getLogger(ResourceHandler.class);
481
        OutputStream out = null;
482
        try
483
        {
484
            out = response.getOutputStream();
485
        }
486
        catch(Exception e)
487
        {
488
            logMetacat.error("Error getting output stream in ResourceHandler.describeObject: " + e.getMessage());
489
            return;
490
        }
491
        response.setStatus(200);
492
        response.setContentType("text/xml");
493
        AuthToken token = new AuthToken(sessionId);
494
        CrudService cs = CrudService.getInstance();
495
        Identifier id = new Identifier();
496
        id.setValue(guid);
497
        try
498
        {
499
            DescribeResponse dr = cs.describe(token, id);
500
            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SZ");
501
            response.addHeader("guid", guid);
502
            response.addHeader("checksum", dr.getDataONE_Checksum().getValue());
503
            response.addHeader("checksum_algorithm", dr.getDataONE_Checksum().getAlgorithm().name());
504
            response.addHeader("content_length", dr.getContent_Length() + "");
505
            response.addHeader("last_modified", dateFormat.format(dr.getLast_Modified()));
506
            response.addHeader("format", dr.getDataONE_ObjectFormat().toString());
507
        }
508
        catch(InvalidRequest ir)
509
        {
510
            serializeException(ir, out);
511
        }
512
        catch(NotImplemented ni)
513
        {
514
            serializeException(ni, out);
515
        }
516
        catch(NotAuthorized na)
517
        {
518
            serializeException(na, out);
519
        }
520
        catch(ServiceFailure sf)
521
        {
522
            serializeException(sf, out);
523
        }
524
        catch(NotFound nf)
525
        {
526
            serializeException(nf, out);
527
        }
528
        catch(InvalidToken it)
529
        {
530
            serializeException(it, out);
531
        }
532
    }
533
    
534
    /**
535
     * get the logs from the CrudService based on passed params.  Available 
536
     * params are token, fromDate, toDate, event.  See 
537
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
538
     * for more info
539
     */
540
    private void getLog()
541
    {
542
        OutputStream out = null;
543
        try
544
        {
545
            out = response.getOutputStream();
546
            response.setStatus(200);
547
            response.setContentType("text/xml");
548
            AuthToken token = new AuthToken(sessionId);
549
            String fromDateS = params.get("fromDate")[0];
550
            System.out.println("param fromDateS: " + fromDateS);
551
            Date fromDate = null;
552
            String toDateS = params.get("toDate")[0];
553
            System.out.println("param toDateS: " + toDateS);
554
            Date toDate = null;
555
            String eventS = params.get("event")[0];
556
            Event event = null;
557
            if(fromDateS != null)
558
            {
559
                //fromDate = dateFormat.parse(fromDateS);
560
                fromDate = parseDateAndConvertToGMT(fromDateS);
561
            }
562
            if(toDateS != null)
563
            {
564
                //toDate = dateFormat.parse(toDateS);
565
                toDate = parseDateAndConvertToGMT(toDateS);
566
            }
567
            if(eventS != null)
568
            {
569
                event = Event.convert(eventS);
570
            }
571
            System.out.println("fromDate: " + fromDate + " toDate: " + toDate);
572
            
573
            System.out.println("calling crudservice.getLogRecords");
574
            Log log = CrudService.getInstance().getLogRecords(token, fromDate, toDate, event);
575
            serializeServiceType(Log.class, log, out);
576
        }
577
        catch(Exception e)
578
        {
579
            String msg = "Could not get logs from CrudService: " + e.getMessage();
580
            response.setStatus(500);
581
            ServiceFailure sf = new ServiceFailure("1490", msg);
582
            logMetacat.error(msg);
583
            e.printStackTrace();
584
            serializeException(sf, out);
585
        }
586
    }
587
    
588
    /**
589
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions  
590
     */
591
    private void initParams() {
592

    
593
        String name = null;
594
        String[] value = null;
595
        Enumeration paramlist = request.getParameterNames();
596
        while (paramlist.hasMoreElements()) {
597
            name = (String) paramlist.nextElement();
598
            value = request.getParameterValues(name);
599
            params.put(name, value);
600
        }
601
    }
602

    
603
    /**
604
     * 
605
     * Load user details of metacat session from the request 
606
     * 
607
     */
608
    private void loadSessionData()
609
      throws Exception
610
    {
611
        SessionData sessionData = RequestUtil.getSessionData(request);
612
        try
613
        {
614
            username = null;
615
            password = null;
616
            groupNames = null;
617
            sessionId = null;
618
            
619
            boolean validSession = false;
620
            SessionService ss = SessionService.getInstance();
621
            System.out.println("sessionData: " + sessionData);
622
            if(sessionData == null)
623
            {
624
                username = "public";
625
                sessionId = "0";
626
                System.out.println("sessiondata is null.  Creating a public session.");
627
                return;
628
            }
629
            
630
            System.out.println("username: " + sessionData.getUserName());
631
            System.out.println("sessionid: " + sessionData.getId());
632
            //validate the session
633
            if(ss.isSessionRegistered(sessionData.getId()) && 
634
               !(sessionData.getUserName().equals("public") || sessionData.getId().equals("0")))
635
            {
636
                validSession = true;
637
            }
638
            
639
            if(validSession)
640
            {
641
                //if the session is valid, set these variables
642
                username = sessionData.getUserName();
643
                password = sessionData.getPassword();
644
                groupNames = sessionData.getGroupNames();
645
                sessionId = sessionData.getId();
646
                System.out.println("setting sessionid to " + sessionId);
647
                System.out.println("username: " + username);
648
            }
649
            
650
            //if the session is not valid or the username is null, set
651
            //username to public
652
            if (username == null) 
653
            {
654
                System.out.println("setting username to public.");
655
                username = "public";
656
            }
657
        }
658
        catch(Exception e)
659
        {
660
            e.printStackTrace();
661
            throw new Exception("Could not load the session data: " + e.getMessage());
662
        }
663
    }
664
    
665
    /**
666
     * generate missing system metadata for any science metadata objects
667
     * that don't already have it. https://trac.dataone.org/ticket/591
668
     * 
669
     * called with POST meta/?op=generatemissingsystemmetadata
670
     */
671
    private void generateMissingSystemMetadata()
672
    {
673
        AuthToken token = new AuthToken(sessionId);
674
        CrudService.getInstance().generateMissingSystemMetadata(token);
675
    }
676

    
677
    /**
678
     *  Earthgrid API > Identifier Service > isRegistered Function : 
679
     *  calls MetacatHandler > handleIdIsRegisteredAction
680
     * @param guid
681
     * @throws IOException
682
     */
683
    private void isRegistered(String guid) throws IOException
684
    {
685
        
686
        // Look up the localId for this guid
687
        IdentifierManager im = IdentifierManager.getInstance();
688
        String localId = "";
689
        try {
690
            localId = im.getLocalId(guid);
691
        } catch (McdbDocNotFoundException e) {
692
            // TODO: Need to return the proper DataONE exception
693
        }
694
        
695
        params.put("docid", new String[] { localId });
696
        PrintWriter out = response.getWriter();
697
        response.setStatus(200);
698
        response.setContentType("text/xml");
699
        handler.handleIdIsRegisteredAction(out, params, response);
700
        out.close();
701
    }
702

    
703
    /**
704
     * Earthgrid API > Identifier Service > getAllDocIds Function : 
705
     * calls MetacatHandler > handleGetAllDocidsAction
706
     * @throws IOException
707
     */
708
    private void getAllDocIds() throws IOException {
709
        PrintWriter out = response.getWriter();
710
        response.setStatus(200);
711
        response.setContentType("text/xml");
712
        handler.handleGetAllDocidsAction(out, params, response);
713
        out.close();
714
    }
715

    
716
    /**
717
     * Earthgrid API > Identifier Service > getNextRevision Function : 
718
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
719
     * @param guid
720
     * @throws IOException
721
     */
722
    private void getNextRevision(String guid) throws IOException 
723
    {
724
        params.put("docid", new String[] { guid });
725
        PrintWriter out = response.getWriter();
726
        response.setStatus(200);
727
        response.setContentType("text/xml");
728
        //handler.handleGetRevisionAndDocTypeAction(out, params);
729

    
730
        try {
731
            // Make sure there is a docid
732
            if (guid == null || guid.equals("")) {
733
                throw new Exception("User didn't specify docid!");
734
            }
735

    
736
            // Look up the localId for this guid
737
            IdentifierManager im = IdentifierManager.getInstance();
738
            String localId = "";
739
            try {
740
                localId = im.getLocalId(guid);
741
            } catch (McdbDocNotFoundException e) {
742
                // TODO: Need to return the proper DataONE exception
743
            }
744
           
745
            // Create a DBUtil object
746
            DBUtil dbutil = new DBUtil();
747
            // Get a rev and doctype
748
            String revAndDocType = dbutil
749
                    .getCurrentRevisionAndDocTypeForGivenDocument(localId);
750
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
751

    
752
            out.println("<?xml version=\"1.0\"?>");
753
            out.print("<next-revision>");
754
            out.print(revision);
755
            out.print("</next-revision>");
756

    
757
        } catch (Exception e) {
758
            // Handle exception
759
            response.setStatus(500);
760
            out.println("<?xml version=\"1.0\"?>");
761
            out.println("<error>");
762
            out.println(e.getMessage());
763
            out.println("</error>");
764
        }
765

    
766
        out.close();
767
    }
768

    
769
    /**
770
     * Earthgrid API > Identifier Service > getNextObject Function : 
771
     * calls MetacatHandler > handleGetMaxDocidAction
772
     * @throws IOException
773
     */
774
    private void getNextObject() throws IOException {
775
        PrintWriter out = response.getWriter();
776
        response.setStatus(200);
777
        response.setContentType("text/xml");
778
        handler.handleGetMaxDocidAction(out, params, response);
779
        out.close();
780
    }
781

    
782
    /**
783
     * Implements REST version of DataONE CRUD API --> get
784
     * @param guid ID of data object to be read
785
     */
786
    private void getObject(String guid) {
787
        CrudService cs = CrudService.getInstance();
788
        cs.setParamsFromRequest(request);
789
        AuthToken token = new AuthToken(sessionId);
790
        OutputStream out = null;
791
        try {
792
            out = response.getOutputStream();
793
            response.setStatus(200);
794
            response.setContentType("text/xml");
795
            if(guid != null)
796
            { //get a specific document                
797
                Identifier id = new Identifier();
798
                id.setValue(guid);
799
                try
800
                {
801
                    if(token == null)
802
                    {
803
                        token = new AuthToken("Public");
804
                    }
805
                    InputStream data = cs.get(token, id);
806
                    IOUtils.copyLarge(data, response.getOutputStream());
807
                }
808
                catch(InvalidToken it)
809
                {
810
                    response.setStatus(500);
811
                    serializeException(it, out); 
812
                }
813
                catch(ServiceFailure sf)
814
                {
815
                    response.setStatus(500);
816
                    serializeException(sf, out); 
817
                }
818
                catch(NotAuthorized na)
819
                {
820
                    response.setStatus(500);
821
                    serializeException(na, out); 
822
                }
823
                catch(NotFound nf)
824
                {
825
                    response.setStatus(500);
826
                    serializeException(nf, out); 
827
                }
828
                catch(NotImplemented ni)
829
                {
830
                    response.setStatus(500);
831
                    serializeException(ni, out); 
832
                }
833
                catch(Exception e)
834
                {
835
                    response.setStatus(500);
836
                    System.out.println("Error with Crud.get().  " +
837
                            "If this is an 'Exception producing data' error, " +
838
                            "go to CrudService.get() for better debugging.  " +
839
                            "Here's the error: " + e.getMessage());
840
                    e.printStackTrace();
841
                    ServiceFailure sf = new ServiceFailure("1030", 
842
                            "IO Error in ResourceHandler.getObject: " + e.getMessage());
843
                    serializeException(sf, out); 
844
                }
845
            }
846
            else
847
            { //call listObjects with specified params
848
                Date startTime = null;
849
                Date endTime = null;
850
                ObjectFormat objectFormat = null;
851
                boolean replicaStatus = false;
852
                int start = 0;
853
                //TODO: make the max count into a const
854
                int count = 1000;
855
                Enumeration paramlist = request.getParameterNames();
856
                while (paramlist.hasMoreElements()) 
857
                { //parse the params and make the crud call
858
                    String name = (String) paramlist.nextElement();
859
                    String[] value = (String[])request.getParameterValues(name);
860
                    /*for(int i=0; i<value.length; i++)
861
                    {
862
                        System.out.println("name: " + name + " value: " + value[i]);
863
                    }*/
864
                    if(name.equals("startTime") && value != null)
865
                    {
866
                        try
867
                        {
868
                          //startTime = dateFormat.parse(value[0]);
869
                            startTime = parseDateAndConvertToGMT(value[0]);
870
                        }
871
                        catch(Exception e)
872
                        {  //if we can't parse it, just don't use the startTime param
873
                            System.out.println("Could not parse startTime: " + value[0]);
874
                            startTime = null;
875
                        }
876
                    }
877
                    else if(name.equals("endTime") && value != null)
878
                    {
879
                        try
880
                        {
881
                          //endTime = dateFormat.parse(value[0]);
882
                            endTime = parseDateAndConvertToGMT(value[0]);
883
                        }
884
                        catch(Exception e)
885
                        {  //if we can't parse it, just don't use the endTime param
886
                            System.out.println("Could not parse endTime: " + value[0]);
887
                            endTime = null;
888
                        }
889
                    }
890
                    else if(name.equals("objectFormat") && value != null)
891
                    {
892
                        objectFormat = ObjectFormat.convert(value[0]);
893
                    }
894
                    else if(name.equals("replicaStatus") && value != null)
895
                    {
896
                        if(value != null && 
897
                           value.length > 0 && 
898
                           (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
899
                        {
900
                            replicaStatus = true;
901
                        }
902
                    }
903
                    else if(name.equals("start") && value != null)
904
                    {
905
                        start = new Integer(value[0]).intValue();
906
                    }
907
                    else if(name.equals("count") && value != null)
908
                    {
909
                        count = new Integer(value[0]).intValue();
910
                    }
911
                }
912
                //make the crud call
913
                System.out.println("token: " + token + " startTime: " + startTime +
914
                        " endtime: " + endTime + " objectFormat: " + 
915
                        objectFormat + " replicaStatus: " + replicaStatus + 
916
                        " start: " + start + " count: " + count);
917
               
918
                ObjectList ol = cs.listObjects(token, startTime, endTime, 
919
                        objectFormat, replicaStatus, start, count);
920
                
921
                StringReader sr = new StringReader(ol.toString());                
922
                out = response.getOutputStream();  
923
                response.setStatus(200);
924
                response.setContentType("text/xml");
925
                // Serialize and write it to the output stream
926
                
927
                try {
928
                    serializeServiceType(ObjectList.class, ol, out);
929
                } catch (JiBXException e) {
930
                    throw new ServiceFailure("1190", "Failed to serialize ObjectList: " + e.getMessage());
931
                }
932
            }
933
        } catch (BaseException e) {
934
                response.setStatus(500);
935
                serializeException(e, out);
936
        } catch (IOException e) {
937
            e.printStackTrace();
938
            response.setStatus(500);
939
            ServiceFailure sf = new ServiceFailure("1030", 
940
                    "IO Error in ResourceHandler.getObject: " + e.getMessage());
941
            serializeException(sf, out); 
942
        } catch(NumberFormatException ne) {
943
            response.setStatus(500);
944
            InvalidRequest ir = new InvalidRequest("1030", "Invalid format for parameter: " + ne.getMessage());
945
            serializeException(ir, out);
946
        } catch (Exception e) {
947
            e.printStackTrace();
948
            response.setStatus(500);
949
            ServiceFailure sf = new ServiceFailure("1030", 
950
                    "Exception " + e.getClass().getName() + " raised while handling listObjects request: " + 
951
                    e.getMessage());
952
            serializeException(sf, out);
953
        }
954
    }
955
    
956
    /**
957
     * parse a date and return it in GMT if it ends with a 'Z'
958
     * @param date
959
     * @return
960
     * @throws ParseException
961
     */
962
    private Date parseDateAndConvertToGMT(String date) throws ParseException
963
    {
964
        try
965
        {   //the format we want
966
            return dateFormat.parse(date);
967
        }
968
        catch(java.text.ParseException pe)
969
        {   //try another legacy format
970
            DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
971
            dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
972
            return dateFormat2.parse(date);
973
        }    
974
    }
975

    
976
    /**
977
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
978
     * @param guid ID of data object to be read
979
     */
980
    private void getSystemMetadataObject(String guid) {
981
        CrudService cs = CrudService.getInstance();
982
        cs.setParamsFromRequest(request);
983
        AuthToken token = new AuthToken(sessionId);
984
        OutputStream out = null;
985
        try {
986
            response.setContentType("text/xml");
987
            response.setStatus(200);
988
            out = response.getOutputStream();
989
            Identifier id = new Identifier();
990
            id.setValue(guid);
991
            SystemMetadata sysmeta = cs.getSystemMetadata(token, id);
992
            
993
            // Serialize and write it to the output stream
994
            try {
995
                //TODO: look at the efficiency of this method.  The system metadata
996
                //is read from metacat (in CrudService) as xml, then serialized
997
                //to a SystemMetadat object, then returned here, then serizlized
998
                //back to XML to be sent to the response.
999
                serializeServiceType(SystemMetadata.class, sysmeta, out);
1000
            } catch (JiBXException e) {
1001
                throw new ServiceFailure("1190", "Failed to serialize SystemMetadata: " + e.getMessage());
1002
            }
1003
        } catch (BaseException e) {
1004
            response.setStatus(500);
1005
                serializeException(e, out);
1006
        } catch (IOException e) {
1007
            response.setStatus(500);
1008
            ServiceFailure sf = new ServiceFailure("1030", 
1009
                    "Error in ResourceHandler.getSystemMetadataObject: " + e.getMessage());
1010
            serializeException(sf, out);
1011
        } finally {
1012
            IOUtils.closeQuietly(out);
1013
        }
1014
    }
1015
    
1016
    /**
1017
     * serialize an object of type to out
1018
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
1019
     * @param object the object to serialize
1020
     * @param out the stream to serialize it to
1021
     * @throws JiBXException
1022
     */
1023
    private void serializeServiceType(Class type, Object object, OutputStream out)
1024
      throws JiBXException
1025
    {
1026
        IBindingFactory bfact = BindingDirectory.getFactory(type);
1027
        IMarshallingContext mctx = bfact.createMarshallingContext();
1028
        mctx.marshalDocument(object, "UTF-8", null, out);
1029
    }
1030
    
1031
    /**
1032
     * deserialize an object of type from is
1033
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
1034
     * @param is the stream to deserialize from
1035
     * @throws JiBXException
1036
     */
1037
    private Object deserializeServiceType(Class type, InputStream is)
1038
      throws JiBXException
1039
    {
1040
        IBindingFactory bfact = BindingDirectory.getFactory(type);
1041
        IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1042
        Object o = (Object) uctx.unmarshalDocument(is, null);
1043
        return o;
1044
    }
1045
    
1046
    /**
1047
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query 
1048
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
1049
     * 
1050
     * NOTE:
1051
     *      This is the only method that uses EcoGrid classes for its implementation.  
1052
     *      It does so because it takes an EcoGrid Query as input, and outputs an
1053
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
1054
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
1055
     *      This creates a circular dependency, because the Metacat classes are needed
1056
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
1057
     *      method.  This circularity could be resolved by moving the EcoGrid classes
1058
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
1059
     *      favor of these REST interfaces, this circular dependency can be eliminated.
1060
     *        
1061
     * @throws Exception
1062
     */
1063
    private void query() throws Exception {
1064
        /*  This block commented out because of the EcoGrid circular dependency.
1065
         *  For now, query will not be supported until the circularity can be
1066
         *  resolved, probably by moving the ecogrid query syntax transformers
1067
         *  directly into the Metacat codebase.  MBJ 2010-02-03
1068
         
1069
        try {
1070
            EcogridQueryParser parser = new EcogridQueryParser(request
1071
                    .getReader());
1072
            parser.parseXML();
1073
            QueryType queryType = parser.getEcogridQuery();
1074
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
1075
                new EcogridJavaToMetacatJavaQueryTransformer();
1076
            QuerySpecification metacatQuery = queryTransformer
1077
                    .transform(queryType);
1078

    
1079
            DBQuery metacat = new DBQuery();
1080

    
1081
            boolean useXMLIndex = (new Boolean(PropertyService
1082
                    .getProperty("database.usexmlindex"))).booleanValue();
1083
            String xmlquery = "query"; // we don't care the query in resultset,
1084
            // the query can be anything
1085
            PrintWriter out = null; // we don't want metacat result, so set out null
1086

    
1087
            // parameter: queryspecification, user, group, usingIndexOrNot
1088
            StringBuffer result = metacat.createResultDocument(xmlquery,
1089
                    metacatQuery, out, username, groupNames, useXMLIndex);
1090

    
1091
            // create result set transfer       
1092
            String saxparser = PropertyService.getProperty("xml.saxparser");
1093
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
1094
                    new StringReader(result.toString()), saxparser, queryType
1095
                            .getNamespace().get_value());
1096
            ResultsetType records = metacatResultsetParser.getEcogridResult();
1097

    
1098
            System.out
1099
                    .println(EcogridResultsetTransformer.toXMLString(records));
1100
            response.setContentType("text/xml");
1101
            out = response.getWriter();
1102
            out.print(EcogridResultsetTransformer.toXMLString(records));
1103

    
1104
        } catch (Exception e) {
1105
            e.printStackTrace();
1106
        }*/
1107
        response.setContentType("text/xml");
1108
        response.setStatus(501);
1109
        PrintWriter out = response.getWriter();
1110
        out.print("<error>Query operation not yet supported by Metacat.</error>");
1111
        out.close();
1112
    }
1113
    
1114
    private String streamToString(InputStream is)
1115
    throws IOException
1116
    {
1117
        return IOUtil.toString(is);
1118
    }
1119

    
1120
    private InputStream stringToStream(String s)
1121
    throws IOException
1122
    {
1123
        ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
1124
        return bais;
1125
    }
1126
    
1127
    /**
1128
     * process a mime multipart message
1129
     * @param is
1130
     * @return
1131
     */
1132
    /*private Hashtable processMMP(InputStream is)
1133
      throws IOException
1134
    {
1135
        //TODO: verify that this is how the RFC for MMP should work
1136
        //SORTAHACK: Since mmp seems to have a bug where large object parts get truncated,
1137
        //parse the stream here.  This has the disavantage of putting the
1138
        //stream into memory.
1139
        InputStream object = null;
1140
        InputStream sysmeta = null;
1141
        String s = IOUtils.toString(is);
1142
        System.out.println("mime: " + s);
1143
        //figure out what the boundary marker is
1144
        String searchString = "boundary=";
1145
        int searchStringIndex = s.indexOf(searchString);
1146
        String boundary = s.substring(searchStringIndex + searchString.length() + 1, 
1147
                s.indexOf("\"", searchStringIndex + searchString.length() + 1));
1148
        boundary = "--" + boundary;
1149
        //System.out.println("boundary is " + boundary);
1150
        
1151
        //find the system metadata
1152
        searchString = "Content-Disposition: attachment; filename=systemmetadata";
1153
        searchStringIndex = s.indexOf(searchString);
1154
        String sm = s.substring(searchStringIndex +
1155
                searchString.length() + 2, 
1156
                s.indexOf(boundary, searchStringIndex));
1157
        sysmeta = new ByteArrayInputStream(sm.getBytes());
1158
        
1159
        //find the object
1160
        searchString = "Content-Disposition: attachment; filename=object";
1161
        searchStringIndex = s.indexOf(searchString);
1162
        String o = s.substring(searchStringIndex +
1163
                searchString.length() + 2, 
1164
                s.indexOf(boundary, searchStringIndex));
1165
        object = new ByteArrayInputStream(o.getBytes());
1166
        
1167
        Hashtable h = new Hashtable();
1168
        h.put("object", object);
1169
        h.put("systemmetadata", sysmeta);
1170
                
1171
        //System.out.println("o: \"" + o + "\"");
1172
        //System.out.println("sm: \"" + sm + "\"");
1173
        return h;
1174
    }
1175
    */
1176
    
1177
    protected static String[] findBoundaryString(InputStream is)
1178
        throws IOException
1179
    {
1180
        String[] endResult = new String[2];
1181
        String boundary = "";
1182
        String searchString = "boundary=";
1183
        boolean doneWithCurrentArray = false;
1184
        byte[] b = new byte[1024];
1185
        int numbytes = is.read(b, 0, 1024);
1186
        while(numbytes != -1)
1187
        {
1188
            String s = new String(b, 0, numbytes);
1189
            
1190
            int[] result = StreamUtil.lookForMatch(searchString, s);
1191
            int searchStringIndex = s.indexOf(searchString);
1192
            if(s.indexOf("\"", searchStringIndex + searchString.length() + 1) == -1)
1193
            { //the end of the boundary is in the next byte array
1194
                boundary = s.substring(searchStringIndex + searchString.length() + 1, s.length());
1195
            }
1196
            else if(!boundary.startsWith("--"))
1197
            { //we can read the whole boundary from this byte array
1198
                boundary = s.substring(searchStringIndex + searchString.length() + 1, 
1199
                    s.indexOf("\"", searchStringIndex + searchString.length() + 1));
1200
                boundary = "--" + boundary;
1201
                endResult[0] = boundary;
1202
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
1203
                        s.length());
1204
                break;
1205
            }
1206
            else
1207
            { //we're now reading the 2nd byte array to get the rest of the boundary
1208
                searchString = "\"";
1209
                searchStringIndex = s.indexOf(searchString);
1210
                boundary += s.substring(0, searchStringIndex);
1211
                boundary = "--" + boundary;
1212
                endResult[0] = boundary;
1213
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
1214
                        s.length());
1215
                break;
1216
            }
1217
        }
1218
        System.out.println("boundary is: " + boundary);
1219
        return endResult;
1220
    }
1221
    
1222
    protected String writeMMPPartToFile(String beginSearch, 
1223
            InputStream is, String boundary, String searchString, File f)
1224
        throws IOException
1225
    {
1226
        Logger logMetacat = Logger.getLogger(ResourceHandler.class);
1227
        logMetacat.info("writing MMP parts");
1228
        //String s = beginSearch;
1229
        String s = null;
1230
        FileOutputStream fos = new FileOutputStream(f);
1231
        int numread = 0;
1232
        byte[] b = new byte[1024];
1233
        String writeString = "";
1234
        
1235
        if(s == null)
1236
        {   //starting with the first part of the stream 
1237
            numread = is.read(b, 0, 1024);
1238
            s = new String(b, 0, numread);
1239
        }
1240
        
1241
        if(beginSearch != null)
1242
        {
1243
            s = beginSearch + s;
1244
        }
1245
        
1246
        boolean useCurrentS = true;
1247
        boolean searchForBoundary = false;
1248
        String seekString = searchString;
1249
        
1250
        while(numread != -1)
1251
        {
1252
            logMetacat.info("////////////////////////iterating");
1253
            logMetacat.info("searchForBoundary: " + searchForBoundary);
1254
            logMetacat.info("useCurrentS: " + useCurrentS);
1255
            logMetacat.info("seekString: " + seekString);
1256
            logMetacat.info("in string: " + s);
1257
            if(searchForBoundary)
1258
            {
1259
                seekString = boundary;
1260
            }
1261
            else
1262
            {
1263
                seekString = searchString;
1264
            }
1265
            
1266
            int[] result = StreamUtil.lookForMatch(seekString, s);
1267
            if(!useCurrentS)
1268
            {
1269
                numread = is.read(b, 0, 1024);
1270
                if(numread != -1)
1271
                {
1272
                    s = new String(b, 0, numread);
1273
                }
1274
                else
1275
                {
1276
                    break;
1277
                }
1278
            }
1279
            
1280
            logMetacat.info("2seekString: " + seekString);
1281
            logMetacat.info("2in string: " + s);
1282
            
1283
            if(result[0] >= 0 && result[1] == seekString.length())
1284
            {
1285
                //searchString is full in s
1286
                logMetacat.info("seekstring is fully in s");
1287
                if(!searchForBoundary)
1288
                {   //we're looking for searchString and we found it
1289
                    //chop off the searchString itself and start writing
1290
                    //until we find boundary
1291
                    s = s.substring(result[0] + result[1], s.length());
1292
                    if(s.length() > 0)
1293
                    {
1294
                        useCurrentS = true;
1295
                    }
1296
                    else
1297
                    {
1298
                        useCurrentS = false;
1299
                    }
1300
                    searchForBoundary = true;
1301
                }
1302
                else
1303
                {   //we're writing, but we found the boundary in this chunk
1304
                    
1305
                    writeString = s.substring(0, result[0]);
1306
                    //System.out.println("writing1: " + writeString);
1307
                    fos.write(writeString.getBytes());
1308
                    //we're done.  break and return;
1309
                    return s.substring(result[0] + result[1], s.length());
1310
                }
1311
            }
1312
            else if(result[0] > 0 && result[1] != seekString.length())
1313
            {
1314
                logMetacat.info("seekstring is partially in s");
1315
                //seekString is partially in s
1316
                //more specifically, the beginning of seekString is at the end
1317
                //of s
1318
                
1319
                //get the next chunk right now, see if the beginning matches
1320
                numread = is.read(b, 0, 1024);
1321
                String s2 = new String(b, 0, numread);
1322
                s += s2;
1323
                useCurrentS = true;
1324
            }
1325
            else
1326
            {
1327
                logMetacat.info("seekstring is not in s");
1328
                //searchString is not in s 
1329
                if(searchForBoundary)
1330
                {
1331
                    //System.out.println("writing2: " + s);
1332
                    fos.write(s.getBytes());
1333
                }
1334
                numread = is.read(b, 0, 1024);
1335
                if(numread != -1)
1336
                {
1337
                    s = new String(b, 0, numread);
1338
                }
1339
                else
1340
                {
1341
                    break;
1342
                }
1343
                useCurrentS = true;
1344
            }
1345
        }
1346
        return "";
1347
    }
1348
    
1349
    protected Hashtable<String, File> writeMMPPartsToFiles(InputStream is)
1350
        throws IOException
1351
    {
1352
        Logger logMetacat = Logger.getLogger(ResourceHandler.class);
1353
        logMetacat.info("Processing Mime Multipart");
1354
        String[] boundaryResults = findBoundaryString(is);
1355
        String boundary = boundaryResults[0];
1356
        String s = boundaryResults[1];
1357
        String[] searchStrings = {
1358
                "Content-Disposition: attachment; filename=systemmetadata\n\n",
1359
                "Content-Disposition: attachment; filename=object\n\n"};
1360
        
1361
        File[] fileArr = getMMPTempFiles();
1362
        Hashtable<String, File> h = new Hashtable<String, File>();
1363
        //System.out.println("==========================Looking for SM");
1364
        //System.out.println("writing sm to " + fileArr[0].getAbsolutePath());
1365
        logMetacat.info("writing mime system metadata to " + fileArr[0].getAbsolutePath());
1366
        s = writeMMPPartToFile(s.trim(), is, boundary, searchStrings[0], fileArr[0]);
1367
        logMetacat.info("writeMMPPartToFile returned '" + s.trim() + "' after processing the system metadata");
1368
        //System.out.println("==========================Looking for Object");
1369
        //System.out.println("writing obj to " + fileArr[1].getAbsolutePath());
1370
        logMetacat.info("writing mime object to " + fileArr[1].getAbsolutePath());
1371
        writeMMPPartToFile(s.trim(), is, boundary, searchStrings[1], fileArr[1]);
1372
        h.put("sysmeta", fileArr[0]);
1373
        h.put("object", fileArr[1]);
1374
        return h;
1375
    }
1376
    
1377
    private static File[] getMMPTempFiles()
1378
        throws IOException
1379
    {
1380
        Logger logMetacat = Logger.getLogger(ResourceHandler.class);
1381
        File tmpDir;
1382
        try
1383
        {
1384
            tmpDir = new File(PropertyService.getProperty("application.tempDir"));
1385
        }
1386
        catch(PropertyNotFoundException pnfe)
1387
        {
1388
            logMetacat.error("ResourceHandler.writeMMPPartstoFiles: " +
1389
                    "application.tmpDir not found.  Using /tmp instead.");
1390
            tmpDir = new File("/tmp");
1391
        }
1392
        long datetimetag = new Date().getTime();
1393
        File smFile = new File(tmpDir, "sm." + datetimetag + ".tmp");
1394
        File objFile = new File(tmpDir, "obj." + datetimetag + ".tmp");
1395
        File[] fileArr = {smFile, objFile};
1396
        return fileArr;
1397
    }
1398
    
1399
    /**
1400
     * return a vector where the first element is a string that represents the system
1401
     * metadata and the 2nd element is an InputStream that is the object
1402
     */
1403
    protected Hashtable<String, File> processMMP(final InputStream is)
1404
      throws IOException
1405
    {
1406
        return writeMMPPartsToFiles(is);
1407
    }
1408
    
1409
    /**
1410
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
1411
     * 
1412
     * @param guid ID of data object to be inserted or updated
1413
     * @throws IOException
1414
     */
1415
    private void putObject(String guid, String action) {
1416
        logMetacat.debug("Entering putObject: " + guid + "/" + action);
1417
        OutputStream out = null;
1418
        Hashtable<String, File> parts = null;
1419
        try {
1420
            out = response.getOutputStream();
1421
            response.setStatus(200);
1422
            response.setContentType("text/xml");
1423
        } catch (IOException e1) {
1424
            logMetacat.error("Could not get the output stream for writing in putObject");
1425
        }
1426
        try {
1427
            
1428
            // Read the incoming data from its Mime Multipart encoding
1429
            logMetacat.debug("Disassembling MIME multipart form");
1430
            InputStream object = null;
1431
            InputStream sysmeta = null;
1432
            
1433
            try
1434
            {
1435
                //String req = IOUtils.toString(request.getInputStream());
1436
                //System.out.println("request: " + req);
1437
                //InputStream reqStr = IOUtils.toInputStream(req);
1438
                InputStream reqStr = request.getInputStream();
1439
                parts = processMMP(reqStr);
1440
                object = new FileInputStream(parts.get("object"));
1441
                sysmeta = new FileInputStream(parts.get("sysmeta"));
1442
                
1443
                String obj = IOUtils.toString(object);
1444
                String sm = IOUtils.toString(sysmeta);
1445
                System.out.println("object: " + obj);
1446
                System.out.println("sm: " + sm);
1447
                object = IOUtils.toInputStream(obj);
1448
                sysmeta = IOUtils.toInputStream(sm);
1449
            }
1450
            catch(IOException ioe)
1451
            {
1452
                throw new ServiceFailure("1202", 
1453
                        "IOException when processing Mime Multipart: " + ioe.getMessage());
1454
            }
1455
            
1456
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
1457

    
1458
                // Check if the objectId exists
1459
                IdentifierManager im = IdentifierManager.getInstance();
1460
                if (im.identifierExists(guid)) {
1461
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
1462
                }
1463

    
1464
                logMetacat.debug("Commence creation...");
1465
                IBindingFactory bfact =
1466
                    BindingDirectory.getFactory(SystemMetadata.class);
1467
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1468
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1469

    
1470
                CrudService cs = CrudService.getInstance();
1471
                AuthToken token = new AuthToken(sessionId); 
1472
                cs.setParamsFromRequest(request);
1473
                Identifier id = new Identifier();
1474
                id.setValue(guid);
1475
                cs.create(token, id, object, m);
1476
                
1477
            } else if (action.equals(FUNCTION_NAME_UPDATE)) { //handle updates
1478
                IdentifierManager im = IdentifierManager.getInstance();
1479
                CrudService cs = CrudService.getInstance();
1480
                Identifier obsoletedGuid = new Identifier();
1481
                Identifier id = new Identifier();
1482
                id.setValue(guid);
1483
                AuthToken token = new AuthToken(sessionId);
1484
                
1485
                //do some checks
1486
                if(params.get("obsoletedGuid") == null)
1487
                {
1488
                    throw new InvalidRequest("1202", "obsoletedGuid must be contained in the request parameters.");
1489
                }
1490
                //get the obsoletedGuid
1491
                String[] obsGuidS = params.get("obsoletedGuid");
1492
                obsoletedGuid.setValue(obsGuidS[0]);
1493
                
1494
                if (!im.identifierExists(obsoletedGuid.getValue())) 
1495
                {
1496
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1497
                }
1498
                
1499
                
1500
                logMetacat.debug("Commence update...");
1501
                
1502
                //get the systemmetadata
1503
                IBindingFactory bfact =
1504
                        BindingDirectory.getFactory(SystemMetadata.class);
1505
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1506
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1507
                
1508
                //do the update
1509
                try
1510
                {
1511
                    cs.setParamsFromRequest(request);
1512
                    Identifier rId = cs.update(token, id, object, obsoletedGuid, m);
1513
                }
1514
                catch(NotFound e)
1515
                {
1516
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1517
                }
1518
                
1519
            } else {
1520
                throw new InvalidRequest("1000", "Operation must be create or update.");
1521
            }
1522
            
1523
            //clean up the MMP files
1524
            //parts.get("sysmeta").delete();
1525
            //parts.get("object").delete();
1526
        } catch (NotAuthorized e) {
1527
            response.setStatus(500);
1528
            serializeException(e, out);
1529
        } catch (InvalidToken e) {
1530
            response.setStatus(500);
1531
            serializeException(e, out);
1532
        } catch (ServiceFailure e) {
1533
            response.setStatus(500);
1534
            serializeException(e, out);
1535
        } catch (IdentifierNotUnique e) {
1536
            response.setStatus(500);
1537
            serializeException(e, out);
1538
        } catch (UnsupportedType e) {
1539
            response.setStatus(500);
1540
            serializeException(e, out);
1541
        } catch (InsufficientResources e) {
1542
            response.setStatus(500);
1543
            serializeException(e, out);
1544
        } catch (InvalidSystemMetadata e) {
1545
            response.setStatus(500);
1546
            serializeException(e, out);
1547
        } catch (NotImplemented e) {
1548
            response.setStatus(500);
1549
            serializeException(e, out);
1550
        } catch (InvalidRequest e) {
1551
            response.setStatus(500);
1552
            serializeException(e, out);
1553
        } /*catch (MessagingException e) {
1554
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1555
            serializeException(sf, out);
1556
        } catch (IOException e) {
1557
            response.setStatus(500);
1558
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1559
            serializeException(sf, out);
1560
        }*/ catch (JiBXException e) {
1561
            response.setStatus(500);
1562
            e.printStackTrace(System.out);
1563
            InvalidSystemMetadata ism = new InvalidSystemMetadata("1080", e.getMessage());
1564
            serializeException(ism, out);
1565
        }
1566
        finally
1567
        {
1568
            if(parts != null)
1569
            {
1570
                //parts.get("sysmeta").delete();
1571
                //parts.get("object").delete();
1572
            }
1573
        }
1574
    }
1575

    
1576
    /**
1577
     * Handle delete 
1578
     * @param guid ID of data object to be deleted
1579
     * @throws IOException
1580
     */
1581
    private void deleteObject(String guid) throws IOException 
1582
    {
1583
        // Look up the localId for this global identifier
1584
        System.out.println("!!!!!!!!!!!!!!!!!deleting object " + guid);
1585
        IdentifierManager im = IdentifierManager.getInstance();
1586
        String localId = "";
1587
        OutputStream out = response.getOutputStream();
1588
        response.setStatus(200);
1589
        try {
1590
            localId = im.getLocalId(guid);
1591
        } catch (McdbDocNotFoundException e) {
1592
            NotFound nf = new NotFound("1340", "Document with guid " + guid + " not found.");
1593
            response.setStatus(404);
1594
            serializeException(nf, out);
1595
        }
1596
       
1597
        AuthToken token = new AuthToken(sessionId);
1598
        CrudService cs = CrudService.getInstance();
1599
        Identifier id = new Identifier();
1600
        id.setValue(guid);
1601
        try
1602
        {
1603
            System.out.println("Calling delete");
1604
            cs.delete(token, id);
1605
        } 
1606
        catch (NotAuthorized e) {
1607
            response.setStatus(500);
1608
            serializeException(e, out);
1609
        } catch (InvalidToken e) {
1610
            response.setStatus(500);
1611
            serializeException(e, out);
1612
        } catch (ServiceFailure e) {
1613
            response.setStatus(500);
1614
            serializeException(e, out);
1615
        } catch (NotImplemented e) {
1616
            response.setStatus(500);
1617
            serializeException(e, out);
1618
        } catch (InvalidRequest e) {
1619
            response.setStatus(500);
1620
            serializeException(e, out);
1621
        } catch(NotFound e) {
1622
            response.setStatus(500);
1623
            serializeException(e, out);
1624
        }
1625
        out.close();
1626
    }
1627
    
1628
    /**
1629
     * set the access perms on a document
1630
     * @throws IOException
1631
     */
1632
    private void setaccess() throws Exception
1633
    {
1634
        try
1635
        {
1636
            String guid = params.get("guid")[0];
1637
            Identifier id = new Identifier();
1638
            id.setValue(guid);
1639
            AuthToken token = new AuthToken(sessionId);
1640
            String principal = params.get("principal")[0];
1641
            String permission = params.get("permission")[0];
1642
            String permissionType = params.get("permissionType")[0];
1643
            String permissionOrder = params.get("permissionOrder")[0];
1644
            String setSystemMetadata = params.get("setsystemmetadata")[0];
1645
            boolean ssm = false;
1646
            if(setSystemMetadata.equals("true") || setSystemMetadata.equals("TRUE") ||
1647
                    setSystemMetadata.equals("yes"))
1648
            {
1649
                ssm = true;
1650
            }
1651
            CrudService cs = CrudService.getInstance();
1652
            //TODO: remove the setsystemmetadata param and set this so the systemmetadata always gets set
1653
            cs.setAccess(token, id, principal, permission, permissionType, permissionOrder, ssm);
1654
        }
1655
        catch(Exception e)
1656
        {
1657
            response.setStatus(500);
1658
            printError("Error setting access in ResourceHandler: " + e.getMessage(), response);
1659
            throw e;
1660
        }
1661
    }
1662

    
1663
    /**
1664
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
1665
     * 
1666
     * @throws IOException
1667
     */
1668
    private void login() throws IOException {
1669
        PrintWriter out = response.getWriter();
1670
        response.setStatus(200);
1671
        response.setContentType("text/xml");
1672
        handler.handleLoginAction(out, params, request, response);
1673
        out.close();
1674
    }
1675

    
1676
    /**
1677
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
1678
     * 
1679
     * @throws IOException
1680
     */
1681
    private void logout() throws IOException {
1682
        PrintWriter out = response.getWriter();
1683
        response.setStatus(200);
1684
        response.setContentType("text/xml");
1685
        handler.handleLogoutAction(out, params, request, response);
1686
        out.close();
1687
    }
1688

    
1689
    /**
1690
     * Prints xml response
1691
     * @param message Message to be displayed
1692
     * @param response Servlet response that xml message will be printed 
1693
     * */
1694
    private void printError(String message, HttpServletResponse response) {
1695
        try {
1696
            logMetacat.error("ResourceHandler: Printing error to servlet response: " + message);
1697
            PrintWriter out = response.getWriter();
1698
            response.setContentType("text/xml");
1699
            out.println("<?xml version=\"1.0\"?>");
1700
            out.println("<error>");
1701
            out.println(message);
1702
            out.println("</error>");
1703
            out.close();
1704
        } catch (IOException e) {
1705
            e.printStackTrace();
1706
        }
1707
    }
1708
    
1709
    private void serializeException(BaseException e, OutputStream out) {
1710
        // TODO: Use content negotiation to determine which return format to use
1711
        response.setContentType("text/xml");
1712
        response.setStatus(e.getCode());
1713
        
1714
        logMetacat.error("ResourceHandler: Serializing exception with code " + e.getCode() + ": " + e.getMessage());
1715
        e.printStackTrace();
1716
        
1717
        try {
1718
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
1719
        } catch (IOException e1) {
1720
            logMetacat.error("Error writing exception to stream. " 
1721
                    + e1.getMessage());
1722
        }
1723
    }
1724

    
1725
}
(2-2/3)