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
            		    //TODO: check if we need this anymore.  Might be superceded
285
            		    //by MetacatPopulator
286
            			generateMissingSystemMetadata();
287
            			status = true;
288
            		}
289
            		else
290
            		{
291
            			String objectId = request.getPathInfo();
292
            			if (objectId != null && objectId.length() > 1) 
293
            			{
294
            				objectId = request.getPathInfo().substring(1);
295
            			}
296
            			getSystemMetadataObject(objectId);
297
            			status = true;
298
            		}
299

    
300
            	} else if (resource.equals(RESOURCE_OBJECTS)) {
301
            		logMetacat.debug("D1 Rest: Starting resource processing...");
302
            		loadSessionData();
303

    
304
            		String objectId = request.getPathInfo();
305
            		if (objectId != null && objectId.length() > 1) 
306
            		{
307
            			objectId = request.getPathInfo().substring(1);
308
            		}
309
            		else
310
            		{
311
            			objectId = null;
312
            		}
313

    
314
            		logMetacat.debug("verb:" + httpVerb);
315

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

    
330
            	} else if (resource.equals(RESOURCE_IDENTIFIER)) {
331

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

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

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

    
361
            	} else if (resource.equals(RESOURCE_LOG)) {
362
            		//handle log events
363
            		if(httpVerb == GET)
364
            		{
365
            			getLog();
366
            			status = true;
367
            		}
368
            		else
369
            		{
370
            		    //change to D1 spec for specifying which http methods are allowed for a resource
371
            			printError("POST, PUT, DELETE is not supported for logs.", response);
372
            			status = true;
373
            		}
374

    
375
            	}
376

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

    
445
        String name = null;
446
        String[] value = null;
447
        Enumeration paramlist = request.getParameterNames();
448
        while (paramlist.hasMoreElements()) {
449
            name = (String) paramlist.nextElement();
450
            value = request.getParameterValues(name);
451
            params.put(name, value);
452
        }
453
    }
454

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

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

    
554
    /**
555
     * Earthgrid API > Identifier Service > getAllDocIds Function : 
556
     * calls MetacatHandler > handleGetAllDocidsAction
557
     * @throws IOException
558
     */
559
    private void getAllDocIds() throws IOException {
560
        PrintWriter out = response.getWriter();
561
        response.setContentType("text/xml");
562
        handler.handleGetAllDocidsAction(out, params, response);
563
        out.close();
564
    }
565

    
566
    /**
567
     * Earthgrid API > Identifier Service > getNextRevision Function : 
568
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
569
     * @param guid
570
     * @throws IOException
571
     */
572
    private void getNextRevision(String guid) throws IOException 
573
    {
574
        params.put("docid", new String[] { guid });
575
        PrintWriter out = response.getWriter();
576
        response.setContentType("text/xml");
577
        //handler.handleGetRevisionAndDocTypeAction(out, params);
578

    
579
        try {
580
            // Make sure there is a docid
581
            if (guid == null || guid.equals("")) {
582
                throw new Exception("User didn't specify docid!");
583
            }
584

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

    
601
            out.println("<?xml version=\"1.0\"?>");
602
            out.print("<next-revision>");
603
            out.print(revision);
604
            out.print("</next-revision>");
605

    
606
        } catch (Exception e) {
607
            // Handle exception
608
            out.println("<?xml version=\"1.0\"?>");
609
            out.println("<error>");
610
            out.println(e.getMessage());
611
            out.println("</error>");
612
        }
613

    
614
        out.close();
615
    }
616

    
617
    /**
618
     * Earthgrid API > Identifier Service > getNextObject Function : 
619
     * calls MetacatHandler > handleGetMaxDocidAction
620
     * @throws IOException
621
     */
622
    private void getNextObject() throws IOException {
623
        PrintWriter out = response.getWriter();
624
        response.setContentType("text/xml");
625
        handler.handleGetMaxDocidAction(out, params, response);
626
        out.close();
627
    }
628

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

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

    
935
            DBQuery metacat = new DBQuery();
936

    
937
            boolean useXMLIndex = (new Boolean(PropertyService
938
                    .getProperty("database.usexmlindex"))).booleanValue();
939
            String xmlquery = "query"; // we don't care the query in resultset,
940
            // the query can be anything
941
            PrintWriter out = null; // we don't want metacat result, so set out null
942

    
943
            // parameter: queryspecification, user, group, usingIndexOrNot
944
            StringBuffer result = metacat.createResultDocument(xmlquery,
945
                    metacatQuery, out, username, groupNames, useXMLIndex);
946

    
947
            // create result set transfer		
948
            String saxparser = PropertyService.getProperty("xml.saxparser");
949
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
950
                    new StringReader(result.toString()), saxparser, queryType
951
                            .getNamespace().get_value());
952
            ResultsetType records = metacatResultsetParser.getEcogridResult();
953

    
954
            System.out
955
                    .println(EcogridResultsetTransformer.toXMLString(records));
956
            response.setContentType("text/xml");
957
            out = response.getWriter();
958
            out.print(EcogridResultsetTransformer.toXMLString(records));
959

    
960
        } catch (Exception e) {
961
            e.printStackTrace();
962
        }*/
963
        response.setContentType("text/xml");
964
        PrintWriter out = response.getWriter();
965
        out.print("<error>Query operation not yet supported by Metacat.</error>");
966
        out.close();
967
    }
968
    
969
    private String streamToString(InputStream is)
970
    throws IOException
971
    {
972
        byte b[] = new byte[1024];
973
        int numread = is.read(b, 0, 1024);
974
        String response = new String();
975
        while(numread != -1)
976
        {
977
            response += new String(b, 0, numread);
978
            numread = is.read(b, 0, 1024);
979
        }
980
        return response;
981
    }
982

    
983
    private InputStream stringToStream(String s)
984
    throws IOException
985
    {
986
        ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
987
        return bais;
988
    }
989
    
990
    /**
991
     * process a mime multipart message
992
     * @param is
993
     * @return
994
     */
995
    private Hashtable processMMP(InputStream is)
996
      throws IOException
997
    {
998
        //TODO: verify that this is how the RFC for MMP should work
999
        //SORTAHACK: Since mmp seems to have a bug where large object parts get truncated,
1000
        //parse the stream here.  This has the disavantage of putting the
1001
        //stream into memory.
1002
        InputStream object = null;
1003
        InputStream sysmeta = null;
1004
        String s = streamToString(is);
1005
        //System.out.println("s: " + s);
1006
        //figure out what the boundary marker is
1007
        String searchString = "boundary=";
1008
        int searchStringIndex = s.indexOf(searchString);
1009
        String boundary = s.substring(searchStringIndex + searchString.length() + 1, 
1010
                s.indexOf("\"", searchStringIndex + searchString.length() + 1));
1011
        boundary = "--" + boundary;
1012
        //System.out.println("boundary is " + boundary);
1013
        
1014
        //find the system metadata
1015
        searchString = "Content-Disposition: attachment; filename=systemmetadata";
1016
        searchStringIndex = s.indexOf(searchString);
1017
        sysmeta = new ByteArrayInputStream(
1018
                s.substring(searchStringIndex +
1019
                        searchString.length(), 
1020
                        s.indexOf(boundary, searchStringIndex)).trim().getBytes());
1021
        
1022
        //find the object
1023
        searchString = "Content-Disposition: attachment; filename=object";
1024
        searchStringIndex = s.indexOf(searchString);
1025
        object = new ByteArrayInputStream(
1026
                s.substring(searchStringIndex +
1027
                        searchString.length(), 
1028
                        s.indexOf(boundary, searchStringIndex)).trim().getBytes());
1029
        
1030
        Hashtable h = new Hashtable();
1031
        h.put("object", object);
1032
        h.put("systemmetadata", sysmeta);
1033
        return h;
1034
    }
1035
    
1036
    
1037
    /**
1038
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
1039
     * 
1040
     * @param guid ID of data object to be inserted or updated
1041
     * @throws IOException
1042
     */
1043
    private void putObject(String guid, String action) {
1044
        logMetacat.debug("Entering putObject: " + guid + "/" + action);
1045
        OutputStream out = null;
1046
        try {
1047
            out = response.getOutputStream();
1048
            response.setContentType("text/xml");
1049
        } catch (IOException e1) {
1050
            logMetacat.error("Could not get the output stream for writing in putObject");
1051
        }
1052
        try {
1053
            
1054
            // Read the incoming data from its Mime Multipart encoding
1055
            logMetacat.debug("Disassembling MIME multipart form");
1056
            InputStream object = null;
1057
            InputStream sysmeta = null;
1058
            
1059
            Hashtable parts = processMMP(request.getInputStream());
1060
            object = (InputStream)parts.get("object");
1061
            sysmeta = (InputStream)parts.get("systemmetadata");
1062
            
1063
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
1064

    
1065
                // Check if the objectId exists
1066
                IdentifierManager im = IdentifierManager.getInstance();
1067
                if (im.identifierExists(guid)) {
1068
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
1069
                }
1070

    
1071
                logMetacat.debug("Commence creation...");
1072
                IBindingFactory bfact =
1073
                    BindingDirectory.getFactory(SystemMetadata.class);
1074
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1075
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1076

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

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

    
1218
    /**
1219
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
1220
     * 
1221
     * @throws IOException
1222
     */
1223
    private void login() throws IOException {
1224
        PrintWriter out = response.getWriter();
1225
        response.setContentType("text/xml");
1226
        handler.handleLoginAction(out, params, request, response);
1227
        out.close();
1228
    }
1229

    
1230
    /**
1231
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
1232
     * 
1233
     * @throws IOException
1234
     */
1235
    private void logout() throws IOException {
1236
        PrintWriter out = response.getWriter();
1237
        response.setContentType("text/xml");
1238
        handler.handleLogoutAction(out, params, request, response);
1239
        out.close();
1240
    }
1241

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

    
1273
}
(2-2/3)