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.dataone.service.exceptions.BaseException;
44
import org.dataone.service.exceptions.IdentifierNotUnique;
45
import org.dataone.service.exceptions.InsufficientResources;
46
import org.dataone.service.exceptions.InvalidRequest;
47
import org.dataone.service.exceptions.InvalidSystemMetadata;
48
import org.dataone.service.exceptions.InvalidToken;
49
import org.dataone.service.exceptions.NotAuthorized;
50
import org.dataone.service.exceptions.NotImplemented;
51
import org.dataone.service.exceptions.ServiceFailure;
52
import org.dataone.service.exceptions.UnsupportedType;
53
import org.dataone.service.exceptions.NotFound;
54
import org.dataone.service.types.*;
55
import org.jibx.runtime.BindingDirectory;
56
import org.jibx.runtime.IBindingFactory;
57
import org.jibx.runtime.IMarshallingContext;
58
import org.jibx.runtime.IUnmarshallingContext;
59
import org.jibx.runtime.JiBXException;
60

    
61
import edu.ucsb.nceas.metacat.DBUtil;
62
import edu.ucsb.nceas.metacat.IdentifierManager;
63
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
64
import edu.ucsb.nceas.metacat.MetacatHandler;
65
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
66
import edu.ucsb.nceas.metacat.dataone.CrudService;
67
import edu.ucsb.nceas.metacat.service.SessionService;
68
import edu.ucsb.nceas.metacat.util.RequestUtil;
69
import edu.ucsb.nceas.metacat.util.SessionData;
70

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

    
144
 * </ul>
145
 * </li>
146
 * 
147
 * <li>
148
 * <h3>EarthGrid Identifier Service</h3><br/>
149
 * 
150
 * <ul>
151
 * <li><h3>isRegistered: </h3>		<br/>
152
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=isregistered</code>   <br/>
153
 * <b>Returns:</b> message in XML format<br/><br/>
154
 * </li>
155

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

    
183
    /**HTTP Verb GET*/
184
    public static final byte GET = 1;
185
    /**HTTP Verb POST*/
186
    public static final byte POST = 2;
187
    /**HTTP Verb PUT*/
188
    public static final byte PUT = 3;
189
    /**HTTP Verb DELETE*/
190
    public static final byte DELETE = 4;
191

    
192
    /*
193
     * API Resources
194
     */
195
    private static final String RESOURCE_OBJECTS = "object";
196
    private static final String RESOURCE_META = "meta";
197
    private static final String RESOURCE_SESSION = "session";
198
    private static final String RESOURCE_IDENTIFIER = "identifier";
199
    private static final String RESOURCE_LOG = "log";
200

    
201
    /*
202
     * API Functions used as URL parameters
203
     */
204
    private static final String FUNCTION_KEYWORD = "op";
205
    private static final String FUNCTION_NAME_LOGIN = "login";
206
    private static final String FUNCTION_NAME_LOGOUT = "logout";
207
    private static final String FUNCTION_NAME_SET_ACCESS = "setaccess";
208
    private static final String FUNCTION_NAME_ISREGISTERED = "isregistered";
209
    private static final String FUNCTION_NAME_GETALLDOCS = "getalldocids";
210
    private static final String FUNCTION_NAME_GETNEXTREV = "getnextrevision";
211
    private static final String FUNCTION_NAME_GETNEXTOBJ = "getnextobject";
212
    private static final String FUNCTION_NAME_INSERT = "insert";
213
    private static final String FUNCTION_NAME_UPDATE = "update";
214
    private static final String FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA = "generatemissingsystemmetadata";
215

    
216
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
217
    
218
    private ServletContext servletContext;
219
    private Logger logMetacat;
220
    private MetacatHandler handler;
221
    private HttpServletRequest request;
222
    private HttpServletResponse response;
223
    private String username;
224
    private String password;
225
    private String sessionId;
226
    private String[] groupNames;
227

    
228
    private Hashtable<String, String[]> params;
229

    
230
    /**Initializes new instance by setting servlet context,request and response*/
231
    public ResourceHandler(ServletContext servletContext,
232
            HttpServletRequest request, HttpServletResponse response) {
233
        this.servletContext = servletContext;
234
        this.request = request;
235
        this.response = response;
236
    }
237

    
238
    /**
239
     * This function is called from REST APU servlet and handles each request to the servlet 
240
     * 
241
     * @param httpVerb (GET, POST, PUT or DELETE)
242
     */
243
    public void handle(byte httpVerb) {
244
        logMetacat = Logger.getLogger(ResourceHandler.class);
245
        try {
246
            String resource = request.getServletPath();
247
            String verb = "";
248
            
249
            System.out.println("handling verb " + httpVerb + " request with resource " + resource);
250
            boolean status = false;
251
            loadSessionData();
252

    
253
            if (resource != null) {
254
                resource = request.getServletPath().substring(1);
255

    
256
                params = new Hashtable<String, String[]>();
257
                initParams();
258

    
259
                Timer timer = new Timer();
260
                handler = new MetacatHandler(timer);
261

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

    
302
                    String objectId = request.getPathInfo();
303
                    if (objectId != null && objectId.length() > 1) 
304
                    {
305
                        objectId = request.getPathInfo().substring(1);
306
                    }
307
                    else
308
                    {
309
                        objectId = null;
310
                    }
311
                    
312
                    logMetacat.debug("verb:" + httpVerb);
313

    
314
                    if (httpVerb == GET) {
315
                        getObject(objectId);
316
                        status = true;
317
                    } else if (httpVerb == POST) {
318
                        putObject(objectId, FUNCTION_NAME_INSERT);
319
                        status = true;
320
                    } else if (httpVerb == PUT) {
321
                        putObject(objectId, FUNCTION_NAME_UPDATE);
322
                        status = true;
323
                    } else if (httpVerb == DELETE) {
324
                        deleteObject(objectId);
325
                        status = true;
326
                    }
327

    
328
                } else if (resource.equals(RESOURCE_IDENTIFIER)) {
329

    
330
                    String identifierId = request.getPathInfo();
331
                    if (identifierId != null && identifierId.length() > 1)
332
                        identifierId = request.getPathInfo().substring(1); //trim the slash
333

    
334
                    if (httpVerb == GET) {
335
                        String op = params.get(FUNCTION_KEYWORD)[0];
336
                        if (op.equals(FUNCTION_NAME_ISREGISTERED)) {
337
                            isRegistered(identifierId);
338
                            status = true;
339
                        } else if (op.equals(FUNCTION_NAME_GETALLDOCS)) {
340
                            getAllDocIds();
341
                            status = true;
342
                        } else if (op.equals(FUNCTION_NAME_GETNEXTREV)) {
343
                            getNextRevision(identifierId);
344
                            status = true;
345
                        } else if (op.equals(FUNCTION_NAME_GETNEXTOBJ)) {
346
                            getNextObject();
347
                            status = true;
348
                        } 
349

    
350
                    } else if (httpVerb == PUT) {
351
                        //Earthgrid API > Identifier Service > addLSID Function 
352
                        printError(
353
                                "This method is not supported by metacat.  To "
354
                                        + "add a new LSID, add a document to metacat.",
355
                                response);
356
                        status = true;
357
                    }
358

    
359
                } else if (resource.equals(RESOURCE_LOG)) {
360
                    //handle log events
361
                    if(httpVerb == GET)
362
                    {
363
                        getLog();
364
                        status = true;
365
                    }
366
                    else
367
                    {
368
                        printError("POST, PUT, DELETE is not supported for logs.", response);
369
                        status = true;
370
                    }
371
                    
372
                }
373
                
374
                if (!status)
375
                    printError("Incorrect parameters!", response);
376
            } else {
377
                printError("Incorrect resource!", response);
378
            }
379
        } catch (Exception e) {
380
            logMetacat.error(e.getMessage());
381
            e.printStackTrace();
382
        }
383
    }
384
    
385
    /**
386
     * get the logs from the CrudService based on passed params.  Available 
387
     * params are token, fromDate, toDate, event.  See 
388
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
389
     * for more info
390
     */
391
    private void getLog()
392
    {
393
        OutputStream out = null;
394
        try
395
        {
396
            out = response.getOutputStream();
397
            AuthToken token = new AuthToken(sessionId);
398
            String fromDateS = params.get("fromDate")[0];
399
            System.out.println("param fromDateS: " + fromDateS);
400
            Date fromDate = null;
401
            String toDateS = params.get("toDate")[0];
402
            System.out.println("param toDateS: " + toDateS);
403
            Date toDate = null;
404
            String eventS = params.get("event")[0];
405
            Event event = null;
406
            if(fromDateS != null)
407
            {
408
                //fromDate = dateFormat.parse(fromDateS);
409
                fromDate = parseDateAndConvertToGMT(fromDateS);
410
            }
411
            if(toDateS != null)
412
            {
413
                //toDate = dateFormat.parse(toDateS);
414
                toDate = parseDateAndConvertToGMT(toDateS);
415
            }
416
            if(eventS != null)
417
            {
418
                event = Event.convert(eventS);
419
            }
420
            System.out.println("fromDate: " + fromDate + " toDate: " + toDate);
421
            
422
            System.out.println("calling crudservice.getLogRecords");
423
            Log log = CrudService.getInstance().getLogRecords(token, fromDate, toDate, event);
424
            serializeServiceType(Log.class, log, out);
425
        }
426
        catch(Exception e)
427
        {
428
            String msg = "Could not get logs from CrudService: " + e.getMessage();
429
            ServiceFailure sf = new ServiceFailure("1490", msg);
430
            logMetacat.error(msg);
431
            e.printStackTrace();
432
            serializeException(sf, out);
433
        }
434
    }
435
    
436
    /**
437
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions  
438
     */
439
    private void initParams() {
440

    
441
        String name = null;
442
        String[] value = null;
443
        Enumeration paramlist = request.getParameterNames();
444
        while (paramlist.hasMoreElements()) {
445
            name = (String) paramlist.nextElement();
446
            value = request.getParameterValues(name);
447
            params.put(name, value);
448
        }
449
    }
450

    
451
    /**
452
     * 
453
     * Load user details of metacat session from the request 
454
     * 
455
     */
456
    private void loadSessionData()
457
      throws Exception
458
    {
459
        SessionData sessionData = RequestUtil.getSessionData(request);
460
        try
461
        {
462
            username = null;
463
            password = null;
464
            groupNames = null;
465
            sessionId = null;
466
            
467
            boolean validSession = false;
468
            SessionService ss = SessionService.getInstance();
469
            System.out.println("sessionData: " + sessionData);
470
            if(sessionData == null)
471
            {
472
                username = "public";
473
                sessionId = "0";
474
                System.out.println("sessiondata is null.  Creating a public session.");
475
                return;
476
            }
477
            
478
            System.out.println("username: " + sessionData.getUserName());
479
            System.out.println("sessionid: " + sessionData.getId());
480
            //validate the session
481
            if(ss.isSessionRegistered(sessionData.getId()) && 
482
               !(sessionData.getUserName().equals("public") || sessionData.getId().equals("0")))
483
            {
484
                validSession = true;
485
            }
486
            
487
            if(validSession)
488
            {
489
                //if the session is valid, set these variables
490
                username = sessionData.getUserName();
491
                password = sessionData.getPassword();
492
                groupNames = sessionData.getGroupNames();
493
                sessionId = sessionData.getId();
494
                System.out.println("setting sessionid to " + sessionId);
495
                System.out.println("username: " + username);
496
            }
497
            
498
            //if the session is not valid or the username is null, set
499
            //username to public
500
            if (username == null) 
501
            {
502
                System.out.println("setting username to public.");
503
                username = "public";
504
            }
505
        }
506
        catch(Exception e)
507
        {
508
            e.printStackTrace();
509
            throw new Exception("Could not load the session data: " + e.getMessage());
510
        }
511
    }
512
    
513
    /**
514
     * generate missing system metadata for any science metadata objects
515
     * that don't already have it. https://trac.dataone.org/ticket/591
516
     * 
517
     * called with POST meta/?op=generatemissingsystemmetadata
518
     */
519
    private void generateMissingSystemMetadata()
520
    {
521
        AuthToken token = new AuthToken(sessionId);
522
        CrudService.getInstance().generateMissingSystemMetadata(token);
523
    }
524

    
525
    /**
526
     *  Earthgrid API > Identifier Service > isRegistered Function : 
527
     *  calls MetacatHandler > handleIdIsRegisteredAction
528
     * @param guid
529
     * @throws IOException
530
     */
531
    private void isRegistered(String guid) throws IOException
532
    {
533
        
534
        // Look up the localId for this guid
535
        IdentifierManager im = IdentifierManager.getInstance();
536
        String localId = "";
537
        try {
538
            localId = im.getLocalId(guid);
539
        } catch (McdbDocNotFoundException e) {
540
            // TODO: Need to return the proper DataONE exception
541
        }
542
        
543
        params.put("docid", new String[] { localId });
544
        PrintWriter out = response.getWriter();
545
        handler.handleIdIsRegisteredAction(out, params, response);
546
        out.close();
547
    }
548

    
549
    /**
550
     * Earthgrid API > Identifier Service > getAllDocIds Function : 
551
     * calls MetacatHandler > handleGetAllDocidsAction
552
     * @throws IOException
553
     */
554
    private void getAllDocIds() throws IOException {
555
        PrintWriter out = response.getWriter();
556
        handler.handleGetAllDocidsAction(out, params, response);
557
        out.close();
558
    }
559

    
560
    /**
561
     * Earthgrid API > Identifier Service > getNextRevision Function : 
562
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
563
     * @param guid
564
     * @throws IOException
565
     */
566
    private void getNextRevision(String guid) throws IOException 
567
    {
568
        params.put("docid", new String[] { guid });
569
        PrintWriter out = response.getWriter();
570
        //handler.handleGetRevisionAndDocTypeAction(out, params);
571

    
572
        try {
573
            // Make sure there is a docid
574
            if (guid == null || guid.equals("")) {
575
                throw new Exception("User didn't specify docid!");
576
            }
577

    
578
            // Look up the localId for this guid
579
            IdentifierManager im = IdentifierManager.getInstance();
580
            String localId = "";
581
            try {
582
                localId = im.getLocalId(guid);
583
            } catch (McdbDocNotFoundException e) {
584
                // TODO: Need to return the proper DataONE exception
585
            }
586
           
587
            // Create a DBUtil object
588
            DBUtil dbutil = new DBUtil();
589
            // Get a rev and doctype
590
            String revAndDocType = dbutil
591
                    .getCurrentRevisionAndDocTypeForGivenDocument(localId);
592
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
593

    
594
            out.println("<?xml version=\"1.0\"?>");
595
            out.print("<next-revision>");
596
            out.print(revision);
597
            out.print("</next-revision>");
598

    
599
        } catch (Exception e) {
600
            // Handle exception
601
            out.println("<?xml version=\"1.0\"?>");
602
            out.println("<error>");
603
            out.println(e.getMessage());
604
            out.println("</error>");
605
        }
606

    
607
        out.close();
608
    }
609

    
610
    /**
611
     * Earthgrid API > Identifier Service > getNextObject Function : 
612
     * calls MetacatHandler > handleGetMaxDocidAction
613
     * @throws IOException
614
     */
615
    private void getNextObject() throws IOException {
616
        PrintWriter out = response.getWriter();
617
        handler.handleGetMaxDocidAction(out, params, response);
618
        out.close();
619
    }
620

    
621
    /**
622
     * Implements REST version of DataONE CRUD API --> get
623
     * @param guid ID of data object to be read
624
     */
625
    private void getObject(String guid) {
626
        CrudService cs = CrudService.getInstance();
627
        cs.setParamsFromRequest(request);
628
        AuthToken token = new AuthToken(sessionId);
629
        OutputStream out = null;
630
        try {
631
            out = response.getOutputStream();
632
            if(guid != null)
633
            { //get a specific document                
634
                Identifier id = new Identifier();
635
                id.setValue(guid);
636
                try
637
                {
638
                    if(token == null)
639
                    {
640
                        token = new AuthToken("Public");
641
                    }
642
                    InputStream data = cs.get(token, id);
643
                    IOUtils.copyLarge(data, response.getOutputStream());
644
                }
645
                catch(InvalidToken it)
646
                {
647
                    serializeException(it, out); 
648
                }
649
                catch(ServiceFailure sf)
650
                {
651
                    serializeException(sf, out); 
652
                }
653
                catch(NotAuthorized na)
654
                {
655
                    serializeException(na, out); 
656
                }
657
                catch(NotFound nf)
658
                {
659
                    serializeException(nf, out); 
660
                }
661
                catch(NotImplemented ni)
662
                {
663
                    serializeException(ni, out); 
664
                }
665
                catch(Exception e)
666
                {
667
                    System.out.println("Error with Crud.get().  " +
668
                            "If this is an 'Exception producing data' error, " +
669
                            "go to CrudService.get() for better debugging.  " +
670
                            "Here's the error: " + e.getMessage());
671
                    e.printStackTrace();
672
                    ServiceFailure sf = new ServiceFailure("1030", 
673
                            "IO Error in ResourceHandler.getObject: " + e.getMessage());
674
                    serializeException(sf, out); 
675
                }
676
            }
677
            else
678
            { //call listObjects with specified params
679
                Date startTime = null;
680
                Date endTime = null;
681
                ObjectFormat objectFormat = null;
682
                boolean replicaStatus = false;
683
                int start = 0;
684
                int count = 1000;
685
                Enumeration paramlist = request.getParameterNames();
686
                while (paramlist.hasMoreElements()) 
687
                { //parse the params and make the crud call
688
                    String name = (String) paramlist.nextElement();
689
                    String[] value = (String[])request.getParameterValues(name);
690
                    /*for(int i=0; i<value.length; i++)
691
                    {
692
                        System.out.println("name: " + name + " value: " + value[i]);
693
                    }*/
694
                    if(name.equals("startTime") && value != null)
695
                    {
696
                        try
697
                        {
698
                          //startTime = dateFormat.parse(value[0]);
699
                            startTime = parseDateAndConvertToGMT(value[0]);
700
                        }
701
                        catch(Exception e)
702
                        {  //if we can't parse it, just don't use the startTime param
703
                            System.out.println("Could not parse startTime: " + value[0]);
704
                            startTime = null;
705
                        }
706
                    }
707
                    else if(name.equals("endTime") && value != null)
708
                    {
709
                        try
710
                        {
711
                          //endTime = dateFormat.parse(value[0]);
712
                            endTime = parseDateAndConvertToGMT(value[0]);
713
                        }
714
                        catch(Exception e)
715
                        {  //if we can't parse it, just don't use the endTime param
716
                            System.out.println("Could not parse endTime: " + value[0]);
717
                            endTime = null;
718
                        }
719
                    }
720
                    else if(name.equals("objectFormat") && value != null)
721
                    {
722
                        objectFormat = ObjectFormat.convert(value[0]);
723
                    }
724
                    else if(name.equals("replicaStatus") && value != null)
725
                    {
726
                        if(value != null && 
727
                           value.length > 0 && 
728
                           (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
729
                        {
730
                            replicaStatus = true;
731
                        }
732
                    }
733
                    else if(name.equals("start") && value != null)
734
                    {
735
                        start = new Integer(value[0]).intValue();
736
                    }
737
                    else if(name.equals("count") && value != null)
738
                    {
739
                        count = new Integer(value[0]).intValue();
740
                    }
741
                }
742
                //make the crud call
743
                /*System.out.println("token: " + token + " startTime: " + startTime +
744
                        " endtime: " + endTime + " objectFormat: " + 
745
                        objectFormat + " replicaStatus: " + replicaStatus + 
746
                        " start: " + start + " count: " + count);
747
                */
748
                ObjectList ol = cs.listObjects(token, startTime, endTime, 
749
                        objectFormat, replicaStatus, start, count);
750
                ol = cs.listObjects(token, startTime, endTime, objectFormat, replicaStatus, start, count);
751
                
752
                StringReader sr = new StringReader(ol.toString());                
753
                out = response.getOutputStream();                
754
                // Serialize and write it to the output stream
755
                
756
                try {
757
                    serializeServiceType(ObjectList.class, ol, out);
758
                } catch (JiBXException e) {
759
                    throw new ServiceFailure("1190", "Failed to serialize ObjectList: " + e.getMessage());
760
                }
761
            }
762
        } catch (BaseException e) {
763
                serializeException(e, out);
764
        } catch (IOException e) {
765
            e.printStackTrace();
766
            ServiceFailure sf = new ServiceFailure("1030", 
767
                    "IO Error in ResourceHandler.getObject: " + e.getMessage());
768
            serializeException(sf, out); 
769
        } catch(NumberFormatException ne) {
770
            InvalidRequest ir = new InvalidRequest("1030", "Invalid format for parameter: " + ne.getMessage());
771
            serializeException(ir, out);
772
        } catch (Exception e) {
773
            e.printStackTrace();
774
            ServiceFailure sf = new ServiceFailure("1030", 
775
                    "Exception " + e.getClass().getName() + " raised while handling listObjects request: " + 
776
                    e.getMessage());
777
            serializeException(sf, out);
778
        }
779
    }
780
    
781
    /**
782
     * parse a date and return it in GMT if it ends with a 'Z'
783
     * @param date
784
     * @return
785
     * @throws ParseException
786
     */
787
    private Date parseDateAndConvertToGMT(String date) throws ParseException
788
    {
789
        try
790
        {   //the format we want
791
            return dateFormat.parse(date);
792
        }
793
        catch(java.text.ParseException pe)
794
        {   //try another legacy format
795
            DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
796
            dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
797
            return dateFormat2.parse(date);
798
        }    
799
        
800
        /*System.out.println("Parsing date " + date);
801
        Date d = dateFormat.parse(date);
802
        
803
        if(date.endsWith("Z"))
804
        {
805
            Calendar lTime = Calendar.getInstance();
806
            lTime.setTime(d);
807
            Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
808
            zTime.set(Calendar.MONTH, lTime.get(Calendar.MONTH));
809
            zTime.set(Calendar.DATE, lTime.get(Calendar.DATE));
810
            zTime.set(Calendar.YEAR, lTime.get(Calendar.YEAR));
811
            zTime.set(Calendar.HOUR, lTime.get(Calendar.HOUR));
812
            zTime.set(Calendar.MINUTE, lTime.get(Calendar.MINUTE));
813
            zTime.set(Calendar.SECOND, lTime.get(Calendar.SECOND));
814
            
815
            System.out.println("date parsed to " + zTime.getTime());
816
            return zTime.getTime();
817
        }
818
        
819
        System.out.println("date parsed, but not converted. returned as " + d);
820
        return d;*/
821
    }
822

    
823
    /**
824
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
825
     * @param guid ID of data object to be read
826
     */
827
    private void getSystemMetadataObject(String guid) {
828
        CrudService cs = CrudService.getInstance();
829
        cs.setParamsFromRequest(request);
830
        AuthToken token = new AuthToken(sessionId);
831
        OutputStream out = null;
832
        try {
833
            out = response.getOutputStream();
834
            Identifier id = new Identifier();
835
            id.setValue(guid);
836
            SystemMetadata sysmeta = cs.getSystemMetadata(token, id);
837
            
838
            // Serialize and write it to the output stream
839
            try {
840
                serializeServiceType(SystemMetadata.class, sysmeta, out);
841
            } catch (JiBXException e) {
842
                throw new ServiceFailure("1190", "Failed to serialize SystemMetadata: " + e.getMessage());
843
            }
844
        } catch (BaseException e) {
845
                serializeException(e, out);
846
        } catch (IOException e) {
847
            ServiceFailure sf = new ServiceFailure("1030", 
848
                    "Error in ResourceHandler.getSystemMetadataObject: " + e.getMessage());
849
            serializeException(sf, out);
850
        } finally {
851
            IOUtils.closeQuietly(out);
852
        }
853
    }
854
    
855
    /**
856
     * serialize an object of type to out
857
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
858
     * @param object the object to serialize
859
     * @param out the stream to serialize it to
860
     * @throws JiBXException
861
     */
862
    private void serializeServiceType(Class type, Object object, OutputStream out)
863
      throws JiBXException
864
    {
865
        IBindingFactory bfact = BindingDirectory.getFactory(type);
866
        IMarshallingContext mctx = bfact.createMarshallingContext();
867
        mctx.marshalDocument(object, "UTF-8", null, out);
868
    }
869
    
870
    /**
871
     * deserialize an object of type from is
872
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
873
     * @param is the stream to deserialize from
874
     * @throws JiBXException
875
     */
876
    private Object deserializeServiceType(Class type, InputStream is)
877
      throws JiBXException
878
    {
879
        IBindingFactory bfact = BindingDirectory.getFactory(type);
880
        IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
881
        Object o = (Object) uctx.unmarshalDocument(is, null);
882
        return o;
883
    }
884
    
885
    /**
886
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query 
887
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
888
     * 
889
     * NOTE:
890
     *      This is the only method that uses EcoGrid classes for its implementation.  
891
     *      It does so because it takes an EcoGrid Query as input, and outputs an
892
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
893
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
894
     *      This creates a circular dependency, because the Metacat classes are needed
895
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
896
     *      method.  This circularity could be resolved by moving the EcoGrid classes
897
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
898
     *      favor of these REST interfaces, this circular dependency can be eliminated.
899
     *        
900
     * @throws Exception
901
     */
902
    private void query() throws Exception {
903
        /*  This block commented out because of the EcoGrid circular dependency.
904
         *  For now, query will not be supported until the circularity can be
905
         *  resolved, probably by moving the ecogrid query syntax transformers
906
         *  directly into the Metacat codebase.  MBJ 2010-02-03
907
         
908
        try {
909
            EcogridQueryParser parser = new EcogridQueryParser(request
910
                    .getReader());
911
            parser.parseXML();
912
            QueryType queryType = parser.getEcogridQuery();
913
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
914
                new EcogridJavaToMetacatJavaQueryTransformer();
915
            QuerySpecification metacatQuery = queryTransformer
916
                    .transform(queryType);
917

    
918
            DBQuery metacat = new DBQuery();
919

    
920
            boolean useXMLIndex = (new Boolean(PropertyService
921
                    .getProperty("database.usexmlindex"))).booleanValue();
922
            String xmlquery = "query"; // we don't care the query in resultset,
923
            // the query can be anything
924
            PrintWriter out = null; // we don't want metacat result, so set out null
925

    
926
            // parameter: queryspecification, user, group, usingIndexOrNot
927
            StringBuffer result = metacat.createResultDocument(xmlquery,
928
                    metacatQuery, out, username, groupNames, useXMLIndex);
929

    
930
            // create result set transfer		
931
            String saxparser = PropertyService.getProperty("xml.saxparser");
932
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
933
                    new StringReader(result.toString()), saxparser, queryType
934
                            .getNamespace().get_value());
935
            ResultsetType records = metacatResultsetParser.getEcogridResult();
936

    
937
            System.out
938
                    .println(EcogridResultsetTransformer.toXMLString(records));
939
            response.setContentType("text/xml");
940
            out = response.getWriter();
941
            out.print(EcogridResultsetTransformer.toXMLString(records));
942

    
943
        } catch (Exception e) {
944
            e.printStackTrace();
945
        }*/
946
        response.setContentType("text/xml");
947
        PrintWriter out = response.getWriter();
948
        out.print("<error>Query operation not yet supported by Metacat.</error>");
949
        out.close();
950
    }
951
    
952
    private String streamToString(InputStream is)
953
    throws IOException
954
    {
955
        byte b[] = new byte[1024];
956
        int numread = is.read(b, 0, 1024);
957
        String response = new String();
958
        while(numread != -1)
959
        {
960
            response += new String(b, 0, numread);
961
            numread = is.read(b, 0, 1024);
962
        }
963
        return response;
964
    }
965

    
966
    private InputStream stringToStream(String s)
967
    throws IOException
968
    {
969
        ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
970
        return bais;
971
    }
972
    
973
    
974
    /**
975
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
976
     * 
977
     * @param guid ID of data object to be inserted or updated
978
     * @throws IOException
979
     */
980
    private void putObject(String guid, String action) {
981
        logMetacat.debug("Entering putObject: " + guid + "/" + action);
982
        OutputStream out = null;
983
        try {
984
            out = response.getOutputStream();
985
        } catch (IOException e1) {
986
            logMetacat.error("Could not get the output stream for writing in putObject");
987
        }
988
        try {
989
            
990
            // Read the incoming data from its Mime Multipart encoding
991
            logMetacat.debug("Disassembling MIME multipart form");
992
            InputStream object = null;
993
            InputStream sysmeta = null;
994
            
995
            //HACK: Since mmp seems to have a bug where large object parts get truncated,
996
            //parse the stream here.  This has the disavantage of putting the
997
            //stream into memory.
998
            String s = streamToString(request.getInputStream());
999
            String searchString = "Content-Disposition: attachment; filename=systemmetadata";
1000
            String endString = "------=_Part";
1001
            int searchStringIndex = s.indexOf(searchString);
1002
            sysmeta = new ByteArrayInputStream(
1003
                    s.substring(searchStringIndex +
1004
                            searchString.length(), 
1005
                            s.indexOf(endString, searchStringIndex)).trim().getBytes());
1006
            
1007
            searchString = "Content-Disposition: attachment; filename=object";
1008
            searchStringIndex = s.indexOf(searchString);
1009
            object = new ByteArrayInputStream(
1010
                    s.substring(searchStringIndex +
1011
                            searchString.length(), 
1012
                            s.indexOf(endString, searchStringIndex)).trim().getBytes());
1013
            
1014
            /*
1015
            //This code should work, however there seems to be a bug in part.getInputStream()
1016
            //where it does not return the entire stream.  Only about the first 6kB 
1017
            //are actually returned.
1018
            MimeMultipart mmp = new MimeMultipart(new InputStreamDataSource("message", request.getInputStream()));
1019
            logMetacat.debug("MMP created.");
1020
            
1021
            //mmp.writeTo(System.out);
1022
            for (int i = 0; i < mmp.getCount(); i++) {
1023
                BodyPart part = mmp.getBodyPart(i);
1024
                String name = part.getFileName();
1025
                logMetacat.debug("Part name is: " + name);
1026
                logMetacat.debug("Part has class name: " + part.getClass().getName());
1027
                
1028
                if (name.equals("object")) {
1029
                    object = part.getInputStream();
1030
                    try
1031
                    {
1032
                        String s = streamToString(object);
1033
                        System.out.println("doc text: " + s);
1034
                        System.out.println("size of doc text is: " + s.getBytes().length);
1035
                        object = stringToStream(s);
1036
                    }
1037
                    catch(Exception e)
1038
                    {}
1039
                    System.out.println("Found object part, size is: " + part.getSize());
1040
                } else if (name.equals("systemmetadata")) {
1041
                    sysmeta = part.getInputStream();
1042
                    try
1043
                    {
1044
                      String s = streamToString(sysmeta);
1045
                      System.out.println("system metadata part: " + s);
1046
                      sysmeta = stringToStream(s);
1047
                    }
1048
                    catch(Exception e){}
1049
                    System.out.println("system metadata part, size is " + part.getSize());
1050
                    logMetacat.debug("Found sysmeta part, size is: " + part.getSize());
1051
                } else {
1052
                    throw new InvalidRequest("1000", "Request had malformed MIME part with name: " + name);
1053
                }
1054
            }*/
1055
            
1056
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
1057

    
1058
                // Check if the objectId exists
1059
                IdentifierManager im = IdentifierManager.getInstance();
1060
                if (im.identifierExists(guid)) {
1061
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
1062
                }
1063

    
1064
                logMetacat.debug("Commence creation...");
1065
                IBindingFactory bfact =
1066
                    BindingDirectory.getFactory(SystemMetadata.class);
1067
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1068
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1069

    
1070
                CrudService cs = CrudService.getInstance();
1071
                AuthToken token = new AuthToken(sessionId); 
1072
                cs.setParamsFromRequest(request);
1073
                Identifier id = new Identifier();
1074
                id.setValue(guid);
1075
                cs.create(token, id, object, m);
1076
                
1077
            } else if (action.equals(FUNCTION_NAME_UPDATE)) { //handle updates
1078
                IdentifierManager im = IdentifierManager.getInstance();
1079
                CrudService cs = CrudService.getInstance();
1080
                Identifier obsoletedGuid = new Identifier();
1081
                Identifier id = new Identifier();
1082
                id.setValue(guid);
1083
                AuthToken token = new AuthToken(sessionId);
1084
                
1085
                //do some checks
1086
                if(params.get("obsoletedGuid") == null)
1087
                {
1088
                    throw new InvalidRequest("1202", "obsoletedGuid must be contained in the request parameters.");
1089
                }
1090
                //get the obsoletedGuid
1091
                String[] obsGuidS = params.get("obsoletedGuid");
1092
                obsoletedGuid.setValue(obsGuidS[0]);
1093
                
1094
                if (!im.identifierExists(obsoletedGuid.getValue())) 
1095
                {
1096
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1097
                }
1098
                
1099
                
1100
                logMetacat.debug("Commence update...");
1101
                
1102
                //get the systemmetadata
1103
                IBindingFactory bfact =
1104
                        BindingDirectory.getFactory(SystemMetadata.class);
1105
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1106
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1107
                
1108
                //do the update
1109
                try
1110
                {
1111
                    cs.setParamsFromRequest(request);
1112
                    Identifier rId = cs.update(token, id, object, obsoletedGuid, m);
1113
                }
1114
                catch(NotFound e)
1115
                {
1116
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1117
                }
1118
                
1119
            } else {
1120
                throw new InvalidRequest("1000", "Operation must be create or update.");
1121
            }
1122
        } catch (NotAuthorized e) {
1123
            serializeException(e, out);
1124
        } catch (InvalidToken e) {
1125
            serializeException(e, out);
1126
        } catch (ServiceFailure e) {
1127
            serializeException(e, out);
1128
        } catch (IdentifierNotUnique e) {
1129
            serializeException(e, out);
1130
        } catch (UnsupportedType e) {
1131
            serializeException(e, out);
1132
        } catch (InsufficientResources e) {
1133
            serializeException(e, out);
1134
        } catch (InvalidSystemMetadata e) {
1135
            serializeException(e, out);
1136
        } catch (NotImplemented e) {
1137
            serializeException(e, out);
1138
        } catch (InvalidRequest e) {
1139
            serializeException(e, out);
1140
        } /*catch (MessagingException e) {
1141
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1142
            serializeException(sf, out);
1143
        }*/ catch (IOException e) {
1144
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1145
            serializeException(sf, out);
1146
        } catch (JiBXException e) {
1147
            e.printStackTrace(System.out);
1148
            InvalidSystemMetadata ism = new InvalidSystemMetadata("1080", e.getMessage());
1149
            serializeException(ism, out);
1150
        }
1151
    }
1152

    
1153
    /**
1154
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction  
1155
     * 
1156
     * @param guid ID of data object to be deleted
1157
     * @throws IOException
1158
     */
1159
    private void deleteObject(String guid) throws IOException 
1160
    {
1161
        // Look up the localId for this global identifier
1162
        IdentifierManager im = IdentifierManager.getInstance();
1163
        String localId = "";
1164
        try {
1165
            localId = im.getLocalId(guid);
1166
        } catch (McdbDocNotFoundException e) {
1167
            // TODO: Need to return the proper DataONE exception
1168
        }
1169
        
1170
        params.put("docid", new String[] { localId });
1171
        PrintWriter out = response.getWriter();
1172
        handler.handleDeleteAction(out, params, request, response, username,
1173
                groupNames);
1174
        out.close();
1175
    }
1176
    
1177
    /**
1178
     * set the access perms on a document
1179
     * @throws IOException
1180
     */
1181
    private void setaccess() throws Exception
1182
    {
1183
        try
1184
        {
1185
            String guid = params.get("guid")[0];
1186
            Identifier id = new Identifier();
1187
            id.setValue(guid);
1188
            AuthToken token = new AuthToken(sessionId);
1189
            String principal = params.get("principal")[0];
1190
            String permission = params.get("permission")[0];
1191
            String permissionType = params.get("permissionType")[0];
1192
            String permissionOrder = params.get("permissionOrder")[0];
1193
            String setSystemMetadata = params.get("setsystemmetadata")[0];
1194
            boolean ssm = false;
1195
            if(setSystemMetadata.equals("true") || setSystemMetadata.equals("TRUE") ||
1196
                    setSystemMetadata.equals("yes"))
1197
            {
1198
                ssm = true;
1199
            }
1200
            CrudService cs = CrudService.getInstance();
1201
            cs.setAccess(token, id, principal, permission, permissionType, permissionOrder, ssm);
1202
        }
1203
        catch(Exception e)
1204
        {
1205
            printError("Error setting access in ResourceHandler: " + e.getMessage(), response);
1206
            throw e;
1207
        }
1208
    }
1209

    
1210
    /**
1211
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
1212
     * 
1213
     * @throws IOException
1214
     */
1215
    private void login() throws IOException {
1216
        PrintWriter out = response.getWriter();
1217
        handler.handleLoginAction(out, params, request, response);
1218
        out.close();
1219
    }
1220

    
1221
    /**
1222
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
1223
     * 
1224
     * @throws IOException
1225
     */
1226
    private void logout() throws IOException {
1227
        PrintWriter out = response.getWriter();
1228
        handler.handleLogoutAction(out, params, request, response);
1229
        out.close();
1230
    }
1231

    
1232
    /**
1233
     * Prints xml response
1234
     * @param message Message to be displayed
1235
     * @param response Servlet response that xml message will be printed 
1236
     * */
1237
    private void printError(String message, HttpServletResponse response) {
1238
        try {
1239
            PrintWriter out = response.getWriter();
1240
            response.setContentType("text/xml");
1241
            out.println("<?xml version=\"1.0\"?>");
1242
            out.println("<error>");
1243
            out.println(message);
1244
            out.println("</error>");
1245
            out.close();
1246
        } catch (IOException e) {
1247
            e.printStackTrace();
1248
        }
1249
    }
1250
    
1251
    private void serializeException(BaseException e, OutputStream out) {
1252
        // TODO: Use content negotiation to determine which return format to use
1253
        response.setContentType("text/xml");
1254
        response.setStatus(e.getCode());
1255
        try {
1256
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
1257
        } catch (IOException e1) {
1258
            logMetacat.error("Error writing exception to stream. " 
1259
                    + e1.getMessage());
1260
        }
1261
    }
1262

    
1263
}
(2-2/3)