Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2011 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.File;
26
import java.io.FileInputStream;
27
import java.io.FileNotFoundException;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.io.PrintWriter;
32
import java.util.Enumeration;
33
import java.util.Hashtable;
34
import java.util.Iterator;
35
import java.util.List;
36
import java.util.Map;
37
import java.util.Timer;
38

    
39
import javax.servlet.ServletContext;
40
import javax.servlet.http.HttpServletRequest;
41
import javax.servlet.http.HttpServletResponse;
42
import javax.xml.parsers.ParserConfigurationException;
43

    
44
import org.apache.commons.fileupload.FileUploadException;
45
import org.apache.commons.io.IOUtils;
46
import org.apache.log4j.Logger;
47
import org.dataone.client.auth.CertificateManager;
48
import org.dataone.mimemultipart.MultipartRequest;
49
import org.dataone.mimemultipart.MultipartRequestResolver;
50
import org.dataone.service.exceptions.BaseException;
51
import org.dataone.service.exceptions.InvalidRequest;
52
import org.dataone.service.exceptions.ServiceFailure;
53
import org.dataone.service.types.v1.AccessPolicy;
54
import org.dataone.service.types.v1.Replica;
55
import org.dataone.service.types.v1.ReplicationPolicy;
56
import org.dataone.service.types.v1.Session;
57
import org.dataone.service.types.v1.SystemMetadata;
58
import org.dataone.service.util.TypeMarshaller;
59
import org.jibx.runtime.JiBXException;
60
import org.xml.sax.SAXException;
61

    
62
import edu.ucsb.nceas.metacat.MetacatHandler;
63
import edu.ucsb.nceas.metacat.properties.PropertyService;
64
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
65
/**
66
 * 
67
 * Base class for handling D1 REST calls in Metacat
68
 * 
69
 * @author leinfelder
70
 */
71
public class D1ResourceHandler {
72

    
73
    /**HTTP Verb GET*/
74
    public static final byte GET = 1;
75
    /**HTTP Verb POST*/
76
    public static final byte POST = 2;
77
    /**HTTP Verb PUT*/
78
    public static final byte PUT = 3;
79
    /**HTTP Verb DELETE*/
80
    public static final byte DELETE = 4;
81
    /**HTTP Verb HEAD*/
82
    public static final byte HEAD = 5;
83

    
84
    /*
85
     * API Resources
86
     */
87
    protected static final String RESOURCE_BASE_URL = "d1";
88

    
89
    protected static final String RESOURCE_OBJECTS = "object";
90
    protected static final String RESOURCE_META = "meta";
91
    protected static final String RESOURCE_LOG = "log";
92
    protected static final String RESOURCE_CHECKSUM = "checksum";
93
    
94
    protected static final String RESOURCE_IS_AUTHORIZED = "isAuthorized";
95
    protected static final String RESOURCE_ACCESS_RULES = "accessRules";
96

    
97
    
98
    /*
99
     * API Functions used as URL parameters
100
     */
101
    protected static final String FUNCTION_NAME_INSERT = "insert";
102
    protected static final String FUNCTION_NAME_UPDATE = "update";
103
    
104
    protected ServletContext servletContext;
105
    protected Logger logMetacat;
106
    protected MetacatHandler handler;
107
    protected HttpServletRequest request;
108
    protected HttpServletResponse response;
109

    
110
    protected Hashtable<String, String[]> params;
111
    protected Map<String, List<String>> multipartparams;
112
    
113
    // D1 certificate-based authentication
114
    protected Session session;
115

    
116
    /**Initializes new instance by setting servlet context,request and response*/
117
    public D1ResourceHandler(ServletContext servletContext,
118
            HttpServletRequest request, HttpServletResponse response) {
119
        this.servletContext = servletContext;
120
        this.request = request;
121
        this.response = response;
122
    }
123

    
124
    /**
125
     * This function is called from REST API servlet and handles each request 
126
     * 
127
     * @param httpVerb (GET, POST, PUT or DELETE)
128
     */
129
    public void handle(byte httpVerb) {
130
        logMetacat = Logger.getLogger(D1ResourceHandler.class);
131
        try {
132
  
133
            // load session from certificate in request
134
            session = CertificateManager.getInstance().getSession(request);
135

    
136
            // initialize the parameters
137
            params = new Hashtable<String, String[]>();
138
            initParams();
139

    
140
            // create the handler for interacting with Metacat
141
            Timer timer = new Timer();
142
            handler = new MetacatHandler(timer);
143

    
144
        } catch (Exception e) {
145
        	// TODO: more D1 structure when failing here
146
        	response.setStatus(400);
147
            printError("Incorrect resource!", response);
148
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
149
        }
150
    }
151
    
152
    protected String parseTrailing(String resource, String token) {
153
    	// get the rest
154
        String extra = null;
155
        if (resource.indexOf(token) != -1) {
156
        	// what comes after the token?
157
            extra = resource.substring(resource.indexOf(token) + token.length());
158
            // remove the slash
159
            if (extra.startsWith("/")) {
160
            	extra = extra.substring(1);
161
            }
162
            // is there anything left?
163
            if (extra.length() == 0) {
164
            	extra = null;
165
            }
166
        }
167
        return extra;
168
    }
169

    
170
    /**
171
     * Parse the replication policy document out of the mime-multipart form data
172
     * 
173
     * @return policy  the encoded policy
174
     * @throws ServiceFailure
175
     * @throws InvalidRequest
176
     * @throws IOException
177
     * @throws InstantiationException
178
     * @throws IllegalAccessException
179
     * @throws JiBXException
180
     */
181
    protected ReplicationPolicy collectReplicationPolicy() 
182
        throws ServiceFailure, InvalidRequest, IOException, InstantiationException, 
183
        IllegalAccessException, JiBXException {
184
        
185
        ReplicationPolicy policy = null;
186
        File tmpDir = getTempDirectory();
187
        MultipartRequest mr = null;
188
        Map<String, File> mmFileParts = null;
189
        File replPolicyFile = null;
190
        InputStream replPolicyStream = null;
191
        
192
        // Read the incoming data from its Mime Multipart encoding
193
        logMetacat.debug("Parsing ReplicationPolicy from the mime multipart entity");
194

    
195
        // handle MMP inputs
196
        MultipartRequestResolver mrr = 
197
            new MultipartRequestResolver(tmpDir.getAbsolutePath(),1000000000, 0);
198
        
199
        try {
200
            mr = mrr.resolveMultipart(request);
201
            logMetacat.debug("Resolved the ReplicationPolicy multipart request.");
202
            
203
        } catch (IOException e) {
204
            throw new ServiceFailure("4882", "Couldn't resolve the multipart request: " +
205
                e.getMessage());
206
            
207
        } catch (FileUploadException e) {
208
            throw new ServiceFailure("4882", "Couldn't resolve the multipart request: " +
209
                    e.getMessage());
210
            
211
        } catch (Exception e) {
212
            throw new ServiceFailure("4882", "Couldn't resolve the multipart request: " +
213
                    e.getMessage());
214
            
215
        }
216
        
217
        // get the map of file parts
218
        mmFileParts = mr.getMultipartFiles();
219
        
220
        if ( mmFileParts == null || mmFileParts.keySet() == null) {
221
            throw new InvalidRequest("4883", "The multipart request must include " +
222
                "a file with the name 'policy'.");
223
            
224
        }
225
        
226
        multipartparams = mr.getMultipartParameters();
227
        replPolicyFile = mmFileParts.get("policy");
228
        
229
        if ( replPolicyFile == null ) {
230
            throw new InvalidRequest("4883", "The multipart request must include " +
231
            "a file with the name 'policy'.");
232
            
233
        }
234
        
235
        
236
        // deserialize the ReplicationPolicy
237
        replPolicyStream = new FileInputStream(replPolicyFile);
238
        policy = TypeMarshaller.unmarshalTypeFromStream(ReplicationPolicy.class, replPolicyStream);
239
        
240
        return policy;
241
        
242
    }
243

    
244
    /**
245
     * Parse the replica metadata document out of the mime-multipart form data
246
     * 
247
     * @return replica  the encoded replica
248
     * @throws ServiceFailure
249
     * @throws InvalidRequest
250
     * @throws IOException
251
     * @throws InstantiationException
252
     * @throws IllegalAccessException
253
     * @throws JiBXException
254
     */
255
    protected Replica collectReplicaMetadata() 
256
        throws ServiceFailure, InvalidRequest {
257
        
258
        Replica replica = null;
259
        File tmpDir = getTempDirectory();
260
        MultipartRequest mr = null;
261
        Map<String, File> mmFileParts = null;
262
        File replicaFile = null;
263
        InputStream replicaStream = null;
264
        
265
        // Read the incoming data from its Mime Multipart encoding
266
        logMetacat.debug("Parsing Replica from the mime multipart entity");
267

    
268
        // handle MMP inputs
269
        MultipartRequestResolver mrr = 
270
            new MultipartRequestResolver(tmpDir.getAbsolutePath(),1000000000, 0);
271
        
272
        try {
273
            mr = mrr.resolveMultipart(request);
274
            logMetacat.debug("Resolved the Replica multipart request.");
275
            
276
        } catch (IOException e) {
277
            throw new ServiceFailure("4852", "Couldn't resolve the multipart request: " +
278
                e.getMessage());
279
            
280
        } catch (FileUploadException e) {
281
            throw new ServiceFailure("4852", "Couldn't resolve the multipart request: " +
282
                    e.getMessage());
283
            
284
        } catch (Exception e) {
285
            throw new ServiceFailure("4852", "Couldn't resolve the multipart request: " +
286
                    e.getMessage());
287
            
288
        }
289
        
290
        // get the map of file parts
291
        mmFileParts = mr.getMultipartFiles();
292
        
293
        if ( mmFileParts == null || mmFileParts.keySet() == null) {
294
            throw new InvalidRequest("4853", "The multipart request must include " +
295
                "a file with the name 'replicaMetadata'.");
296
            
297
        }
298
        
299
        multipartparams = mr.getMultipartParameters();
300
        replicaFile = mmFileParts.get("replicaMetadata");
301
        
302
        if ( replicaFile == null ) {
303
            throw new InvalidRequest("4853", "The multipart request must include " +
304
            "a file with the name 'replicaMetadata'.");
305
            
306
        }
307
        
308
        
309
        // deserialize the ReplicationPolicy
310
        try {
311
            replicaStream = new FileInputStream(replicaFile);
312
        } catch (FileNotFoundException e) {
313
            throw new ServiceFailure("4852", "Couldn't find the multipart file: " +
314
                    e.getMessage());
315
            
316
        }
317
        
318
        try {
319
            replica = TypeMarshaller.unmarshalTypeFromStream(Replica.class, replicaStream);
320
        } catch (IOException e) {
321
            throw new ServiceFailure("4852", "Couldn't deserialize the replica document: " +
322
                    e.getMessage());
323
            
324
        } catch (InstantiationException e) {
325
            throw new ServiceFailure("4852", "Couldn't deserialize the replica document: " +
326
                    e.getMessage());
327
            
328
        } catch (IllegalAccessException e) {
329
            throw new ServiceFailure("4852", "Couldn't deserialize the replica document: " +
330
                    e.getMessage());
331
            
332
        } catch (JiBXException e) {
333
            throw new ServiceFailure("4852", "Couldn't deserialize the replica document: " +
334
                    e.getMessage());
335
            
336
        }
337
        
338
        return replica;
339
        
340
    }
341
    
342
    protected AccessPolicy collectAccessPolicy() 
343
        throws IOException, ServiceFailure, InvalidRequest, JiBXException, 
344
        InstantiationException, IllegalAccessException, ParserConfigurationException, 
345
        SAXException  {
346
		
347
		// Read the incoming data from its Mime Multipart encoding
348
		logMetacat.debug("Disassembling MIME multipart form");
349
		InputStream ap = null;
350

    
351
		// handle MMP inputs
352
		File tmpDir = getTempDirectory();
353
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
354
		MultipartRequestResolver mrr = 
355
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
356
		MultipartRequest mr = null;
357
		try {
358
			mr = mrr.resolveMultipart(request);
359
		} catch (Exception e) {
360
			throw new ServiceFailure("2161", 
361
					"Could not resolve multipart: " + e.getMessage());
362
		}
363
		logMetacat.debug("resolved multipart request");
364
		Map<String, File> files = mr.getMultipartFiles();
365
		if (files == null || files.keySet() == null) {
366
			throw new InvalidRequest("2163",
367
					"must have multipart file with name 'accessPolicy'");
368
		}
369
		logMetacat.debug("got multipart files");
370

    
371
		multipartparams = mr.getMultipartParameters();
372

    
373
		File apFile = files.get("accessPolicy");
374
		if (apFile == null) {
375
			throw new InvalidRequest("2163",
376
					"Missing the required file-part 'accessPolicy' from the multipart request.");
377
		}
378
		logMetacat.debug("apFile: " + apFile.getAbsolutePath());
379
		ap = new FileInputStream(apFile);
380
	
381
		AccessPolicy accessPolicy = TypeMarshaller.unmarshalTypeFromStream(AccessPolicy.class, ap);
382
		return accessPolicy;
383
	}
384
    
385
    protected SystemMetadata collectSystemMetadata() 
386
        throws IOException, FileUploadException, ServiceFailure, InvalidRequest, 
387
        JiBXException, InstantiationException, IllegalAccessException  {
388
		
389
		// Read the incoming data from its Mime Multipart encoding
390
		logMetacat.debug("Disassembling MIME multipart form");
391
		InputStream sysmeta = null;
392

    
393
		// handle MMP inputs
394
		File tmpDir = getTempDirectory();
395
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
396
		MultipartRequestResolver mrr = 
397
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
398
		MultipartRequest mr = null;
399
		try {
400
			mr = mrr.resolveMultipart(request);
401
		} catch (Exception e) {
402
			throw new ServiceFailure("1202", 
403
					"Could not resolve multipart: " + e.getMessage());
404
		}
405
		logMetacat.debug("resolved multipart request");
406
		Map<String, File> files = mr.getMultipartFiles();
407
		if (files == null) {
408
			throw new ServiceFailure("1202",
409
					"register meta must have multipart file with name 'sysmeta'");
410
		}
411
		logMetacat.debug("got multipart files");
412

    
413
		if (files.keySet() == null) {
414
			logMetacat.error("No file keys in MMP request.");
415
			throw new ServiceFailure(
416
					"1202",
417
					"No file keys found in MMP.  "
418
							+ "register meta must have multipart file with name 'sysmeta'");
419
		}
420

    
421
		// for logging purposes, dump out the key-value pairs that
422
		// constitute the request
423
		// 3 types exist: request params, multipart params, and
424
		// multipart files
425
		Iterator it = files.keySet().iterator();
426
		logMetacat.debug("iterating through request parts: " + it);
427
		while (it.hasNext()) {
428
			String key = (String) it.next();
429
			logMetacat.debug("files key: " + key);
430
			logMetacat.debug("files value: " + files.get(key));
431
		}
432

    
433
		multipartparams = mr.getMultipartParameters();
434
		it = multipartparams.keySet().iterator();
435
		while (it.hasNext()) {
436
			String key = (String) it.next();
437
			logMetacat.debug("multipartparams key: " + key);
438
			logMetacat.debug("multipartparams value: " + multipartparams.get(key));
439
		}
440

    
441
		it = params.keySet().iterator();
442
		while (it.hasNext()) {
443
			String key = (String) it.next();
444
			logMetacat.debug("param key: " + key);
445
			logMetacat.debug("param value: " + params.get(key));
446
		}
447
		logMetacat.debug("done iterating the request...");
448

    
449
		File smFile = files.get("sysmeta");
450
		if (smFile == null) {
451
			throw new InvalidRequest("1102",
452
					"Missing the required file-part 'sysmeta' from the multipart request.");
453
		}
454
		logMetacat.debug("smFile: " + smFile.getAbsolutePath());
455
		sysmeta = new FileInputStream(smFile);
456
	
457
		logMetacat.debug("Commence creation...");
458
		SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
459
		return systemMetadata;
460
	}
461
    
462
    protected Map<String, File> collectMultipartFiles() 
463
        throws ServiceFailure, InvalidRequest {
464
    	
465
        // Read the incoming data from its Mime Multipart encoding
466
        logMetacat.debug("Disassembling MIME multipart form");
467
        InputStream object = null;
468
        InputStream sysmeta = null;
469
        
470
        
471
        // handle MMP inputs
472
        File tmpDir = getTempDirectory();
473
        logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
474
        MultipartRequestResolver mrr = 
475
        	new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
476
        MultipartRequest mr = null;
477
		try {
478
			mr = mrr.resolveMultipart(request);
479
		} catch (Exception e) {
480
            throw new ServiceFailure("1202", 
481
            		"Could not resolve multipart files: " + e.getMessage());
482
		}
483
        logMetacat.debug("resolved multipart request");
484
        Map<String, File> files = mr.getMultipartFiles();
485
        if (files == null) {
486
            throw new ServiceFailure("1202", "create/update must have multipart files with names 'object' and 'sysmeta'");
487
        }
488
        logMetacat.debug("got multipart files");
489
        
490
        if (files.keySet() == null) {
491
            logMetacat.error("No file keys in MMP request.");
492
            throw new ServiceFailure("1202", "No file keys found in MMP.  " +
493
                    "create/update must have multipart files with names 'object' and 'sysmeta'");
494
        }
495

    
496
		// for logging purposes, dump out the key-value pairs that constitute the request
497
		// 3 types exist: request params, multipart params, and multipart files
498
        Iterator it = files.keySet().iterator();
499
        logMetacat.debug("iterating through files");
500
        while (it.hasNext()) {
501
            String key = (String)it.next();
502
            logMetacat.debug("files key: " + key);
503
            logMetacat.debug("files value: " + files.get(key));
504
        }
505
        
506
        multipartparams = mr.getMultipartParameters();
507
        it = multipartparams.keySet().iterator();
508
        logMetacat.debug("iterating through multipartparams");
509
        while (it.hasNext()) {
510
            String key = (String)it.next();
511
            logMetacat.debug("multipartparams key: " + key);
512
            logMetacat.debug("multipartparams value: " + multipartparams.get(key));
513
        }
514
        
515
        it = params.keySet().iterator();
516
        logMetacat.debug("iterating through params");
517
        while (it.hasNext()) {
518
            String key = (String)it.next();
519
            logMetacat.debug("param key: " + key);
520
            logMetacat.debug("param value: " + params.get(key));
521
        }
522
        logMetacat.debug("done iterating the request...");
523

    
524
        File smFile = files.get("sysmeta");
525
		if (smFile == null) {
526
		    throw new InvalidRequest("1102", "Missing the required file-part 'sysmeta' from the multipart request.");
527
		}
528
        logMetacat.debug("smFile: " + smFile.getAbsolutePath());
529
        File objFile = files.get("object");
530
		if (objFile == null) {
531
		    throw new InvalidRequest("1102", "Missing the required file-part 'object' from the multipart request.");
532
		}
533
        logMetacat.debug("objectfile: " + objFile.getAbsolutePath());
534
        
535
        return files;
536
    }
537
    
538
		/**
539
     *  copies request parameters to a hashtable which is given as argument to 
540
     *  native metacathandler functions  
541
     */
542
    protected void initParams() {
543

    
544
        String name = null;
545
        String[] value = null;
546
        Enumeration paramlist = request.getParameterNames();
547
        while (paramlist.hasMoreElements()) {
548
            name = (String) paramlist.nextElement();
549
            value = request.getParameterValues(name);
550
            params.put(name, value);
551
        }
552
    }
553
   
554
    /**
555
     * locate the boundary marker for an MMP
556
     * @param is
557
     * @return
558
     * @throws IOException
559
     */
560
    protected static String[] findBoundaryString(InputStream is)
561
        throws IOException {
562
        String[] endResult = new String[2];
563
        String boundary = "";
564
        String searchString = "boundary=";
565
        byte[] b = new byte[1024];
566
        int numbytes = is.read(b, 0, 1024);
567

    
568
        while(numbytes != -1)
569
        {
570
            String s = new String(b, 0, numbytes);
571
            int searchStringIndex = s.indexOf(searchString);
572
            
573
            if(s.indexOf("\"", searchStringIndex + searchString.length() + 1) == -1)
574
            { //the end of the boundary is in the next byte array
575
                boundary = s.substring(searchStringIndex + searchString.length() + 1, s.length());
576
            }
577
            else if(!boundary.startsWith("--"))
578
            { //we can read the whole boundary from this byte array
579
                boundary = s.substring(searchStringIndex + searchString.length() + 1, 
580
                    s.indexOf("\"", searchStringIndex + searchString.length() + 1));
581
                boundary = "--" + boundary;
582
                endResult[0] = boundary;
583
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
584
                        s.length());
585
                break;
586
            }
587
            else
588
            { //we're now reading the 2nd byte array to get the rest of the boundary
589
                searchString = "\"";
590
                searchStringIndex = s.indexOf(searchString);
591
                boundary += s.substring(0, searchStringIndex);
592
                boundary = "--" + boundary;
593
                endResult[0] = boundary;
594
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
595
                        s.length());
596
                break;
597
            }
598
        }
599
        return endResult;
600
    }
601
    
602
    /**
603
     * return the directory where temp files are stored
604
     * @return
605
     */
606
    protected static File getTempDirectory()
607
    {
608
        File tmpDir = null;
609
        Logger logMetacat = Logger.getLogger(D1ResourceHandler.class);
610
        try {
611
            tmpDir = new File(PropertyService.getProperty("application.tempDir"));
612
        }
613
        catch(PropertyNotFoundException pnfe) {
614
            logMetacat.error("D1ResourceHandler.writeMMPPartstoFiles: " +
615
                    "application.tmpDir not found.  Using /tmp instead.");
616
            tmpDir = new File("/tmp");
617
        }
618
        return tmpDir;
619
    }
620
    
621
    /**
622
     * Prints xml response
623
     * @param message Message to be displayed
624
     * @param response Servlet response that xml message will be printed 
625
     * */
626
    protected void printError(String message, HttpServletResponse response) {
627
        try {
628
            logMetacat.error("D1ResourceHandler: Printing error to servlet response: " + message);
629
            PrintWriter out = response.getWriter();
630
            response.setContentType("text/xml");
631
            out.println("<?xml version=\"1.0\"?>");
632
            out.println("<error>");
633
            out.println(message);
634
            out.println("</error>");
635
            out.close();
636
        } catch (IOException e) {
637
            e.printStackTrace();
638
        }
639
    }
640
    
641
    /**
642
     * serialize a D1 exception using jibx
643
     * @param e
644
     * @param out
645
     */
646
    protected void serializeException(BaseException e, OutputStream out) {
647
        // TODO: Use content negotiation to determine which return format to use
648
        response.setContentType("text/xml");
649
        response.setStatus(e.getCode());
650
        
651
        logMetacat.error("D1ResourceHandler: Serializing exception with code " + e.getCode() + ": " + e.getMessage());
652
        e.printStackTrace();
653
        
654
        try {
655
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
656
        } catch (IOException e1) {
657
            logMetacat.error("Error writing exception to stream. " 
658
                    + e1.getMessage());
659
        }
660
    }
661

    
662
}
(4-4/9)