Project

General

Profile

1 6264 leinfelder
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author$'
7
 *     '$Date$'
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.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
30
import java.text.DateFormat;
31
import java.text.ParseException;
32
import java.text.SimpleDateFormat;
33
import java.util.Date;
34
import java.util.Enumeration;
35
import java.util.Map;
36 6297 cjones
import java.util.TimeZone;
37 6679 leinfelder
import java.util.concurrent.ExecutorService;
38
import java.util.concurrent.Executors;
39 6264 leinfelder
40
import javax.servlet.ServletContext;
41
import javax.servlet.http.HttpServletRequest;
42
import javax.servlet.http.HttpServletResponse;
43 6472 leinfelder
import javax.xml.parsers.ParserConfigurationException;
44 6264 leinfelder
45 6273 leinfelder
import org.apache.commons.fileupload.FileUploadException;
46 6264 leinfelder
import org.apache.commons.io.IOUtils;
47
import org.apache.log4j.Logger;
48
import org.dataone.client.ObjectFormatCache;
49 6472 leinfelder
import org.dataone.mimemultipart.MultipartRequest;
50
import org.dataone.mimemultipart.MultipartRequestResolver;
51 6264 leinfelder
import org.dataone.service.exceptions.BaseException;
52
import org.dataone.service.exceptions.IdentifierNotUnique;
53
import org.dataone.service.exceptions.InsufficientResources;
54
import org.dataone.service.exceptions.InvalidRequest;
55
import org.dataone.service.exceptions.InvalidSystemMetadata;
56
import org.dataone.service.exceptions.InvalidToken;
57
import org.dataone.service.exceptions.NotAuthorized;
58
import org.dataone.service.exceptions.NotFound;
59
import org.dataone.service.exceptions.NotImplemented;
60
import org.dataone.service.exceptions.ServiceFailure;
61
import org.dataone.service.exceptions.SynchronizationFailed;
62
import org.dataone.service.exceptions.UnsupportedType;
63 6366 leinfelder
import org.dataone.service.types.v1.Checksum;
64
import org.dataone.service.types.v1.DescribeResponse;
65
import org.dataone.service.types.v1.Event;
66
import org.dataone.service.types.v1.Identifier;
67
import org.dataone.service.types.v1.Log;
68
import org.dataone.service.types.v1.MonitorList;
69
import org.dataone.service.types.v1.Node;
70
import org.dataone.service.types.v1.NodeReference;
71
import org.dataone.service.types.v1.ObjectFormat;
72
import org.dataone.service.types.v1.ObjectFormatIdentifier;
73
import org.dataone.service.types.v1.ObjectList;
74
import org.dataone.service.types.v1.Permission;
75
import org.dataone.service.types.v1.Subject;
76
import org.dataone.service.types.v1.SystemMetadata;
77 6803 leinfelder
import org.dataone.service.util.Constants;
78 6469 leinfelder
import org.dataone.service.util.DateTimeMarshaller;
79 6472 leinfelder
import org.dataone.service.util.ExceptionHandler;
80 6367 leinfelder
import org.dataone.service.util.TypeMarshaller;
81 6264 leinfelder
import org.jibx.runtime.JiBXException;
82 6472 leinfelder
import org.xml.sax.SAXException;
83 6264 leinfelder
84
import edu.ucsb.nceas.metacat.dataone.MNodeService;
85
86
/**
87
 * MN REST service implementation handler
88
 *
89
 * ******************
90
 * MNCore -- DONE
91
 * 		ping() - GET /d1/mn/monitor/ping
92
 * 		log() - GET /d1/mn/log
93
 * 		**getObjectStatistics() - GET /d1/mn/monitor/object
94
 * 		getOperationsStatistics - GET /d1/mn/monitor/event
95
 * 		**getStatus - GET /d1/mn/monitor/status
96
 * 		getCapabilities() - GET /d1/mn/ and /d1/mn/node
97
 *
98
 * 	MNRead -- DONE
99
 * 		get() - GET /d1/mn/object/PID
100
 * 		getSystemMetadata() - GET /d1/mn/meta/PID
101
 * 		describe() - HEAD /d1/mn/object/PID
102
 * 		getChecksum() - GET /d1/mn/checksum/PID
103
 * 		listObjects() - GET /d1/mn/object
104
 * 		synchronizationFailed() - POST /d1/mn/error
105
 *
106
 * 	MNAuthorization -- DONE
107
 * 		isAuthorized() - GET /d1/mn/isAuthorized/PID
108
 * 		setAccessPolicy() - PUT /d1/mn/accessRules/PID
109
 *
110 6297 cjones
 * 	MNStorage - DONE
111 6264 leinfelder
 * 		create() - POST /d1/mn/object/PID
112
 * 		update() - PUT /d1/mn/object/PID
113
 * 		delete() - DELETE /d1/mn/object/PID
114 6619 cjones
 *    systemMetadataChanged() - POST /dirtySystemMetadata/PID
115 6264 leinfelder
 *
116
 * 	MNReplication
117
 * 		replicate() - POST /d1/mn/replicate
118 6604 cjones
 *    getReplica() - GET /d1/mn/replica
119 6264 leinfelder
 *
120
 * ******************
121
 * @author leinfelder
122
 *
123
 */
124 6267 leinfelder
public class MNResourceHandler extends D1ResourceHandler{
125 6264 leinfelder
126
    // MN-specific API Resources
127
    protected static final String RESOURCE_MONITOR = "monitor";
128
    protected static final String RESOURCE_REPLICATE = "replicate";
129 6604 cjones
    protected static final String RESOURCE_REPLICAS = "replica";
130 6264 leinfelder
    protected static final String RESOURCE_NODE = "node";
131
    protected static final String RESOURCE_ERROR = "error";
132 6619 cjones
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
133 6264 leinfelder
134
    /**
135
     * Initializes new instance by setting servlet context,request and response
136
     * */
137
    public MNResourceHandler(ServletContext servletContext,
138
            HttpServletRequest request, HttpServletResponse response) {
139
    	super(servletContext, request, response);
140
        logMetacat = Logger.getLogger(MNResourceHandler.class);
141
    }
142
143
    /**
144 6271 leinfelder
     * This function is called from REST API servlet and handles each request to the servlet
145 6264 leinfelder
     *
146
     * @param httpVerb (GET, POST, PUT or DELETE)
147
     */
148 6271 leinfelder
    @Override
149 6264 leinfelder
    public void handle(byte httpVerb) {
150 6271 leinfelder
    	// prepare the handler
151
    	super.handle(httpVerb);
152
153 6264 leinfelder
        try {
154 6284 leinfelder
        	// get the resource
155
            String resource = request.getPathInfo();
156
            resource = resource.substring(resource.indexOf("/") + 1);
157 6264 leinfelder
158 6284 leinfelder
            // default to node info
159
            if (resource.equals("")) {
160 6264 leinfelder
                resource = RESOURCE_NODE;
161
            }
162 6284 leinfelder
163
            // get the rest of the path info
164
            String extra = null;
165 6264 leinfelder
166 6272 leinfelder
            logMetacat.debug("handling verb " + httpVerb + " request with resource '" + resource + "'");
167
            logMetacat.debug("resource: '" + resource + "'");
168 6264 leinfelder
            boolean status = false;
169
170
            if (resource != null) {
171
172 6284 leinfelder
                if (resource.startsWith(RESOURCE_NODE)) {
173 6264 leinfelder
                    // node response
174
                    node();
175
                    status = true;
176 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
177 6264 leinfelder
                    if (httpVerb == GET) {
178 6514 leinfelder
                    	// after the command
179
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
180 6264 leinfelder
	                	// check the access rules
181 6284 leinfelder
	                    isAuthorized(extra);
182 6264 leinfelder
	                    status = true;
183 6272 leinfelder
	                    logMetacat.debug("done getting access");
184 6264 leinfelder
                    }
185 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_META)) {
186 6272 leinfelder
                    logMetacat.debug("Using resource 'meta'");
187 6264 leinfelder
                    // get
188
                    if (httpVerb == GET) {
189 6514 leinfelder
                    	// after the command
190
                        extra = parseTrailing(resource, RESOURCE_META);
191 6284 leinfelder
                        getSystemMetadataObject(extra);
192 6264 leinfelder
                        status = true;
193
                    }
194
195 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
196 6272 leinfelder
                    logMetacat.debug("Using resource 'object'");
197 6514 leinfelder
                    // after the command
198
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
199 6284 leinfelder
                    logMetacat.debug("objectId: " + extra);
200 6264 leinfelder
                    logMetacat.debug("verb:" + httpVerb);
201
202
                    if (httpVerb == GET) {
203 6284 leinfelder
                        getObject(extra);
204 6264 leinfelder
                        status = true;
205
                    } else if (httpVerb == POST) {
206 6284 leinfelder
                        putObject(extra, FUNCTION_NAME_INSERT);
207 6264 leinfelder
                        status = true;
208
                    } else if (httpVerb == PUT) {
209 6284 leinfelder
                        putObject(extra, FUNCTION_NAME_UPDATE);
210 6264 leinfelder
                        status = true;
211
                    } else if (httpVerb == DELETE) {
212 6284 leinfelder
                        deleteObject(extra);
213 6264 leinfelder
                        status = true;
214
                    } else if (httpVerb == HEAD) {
215 6284 leinfelder
                        describeObject(extra);
216 6264 leinfelder
                        status = true;
217
                    }
218
219 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_LOG)) {
220 6272 leinfelder
                    logMetacat.debug("Using resource 'log'");
221 6264 leinfelder
                    // handle log events
222
                    if (httpVerb == GET) {
223
                        getLog();
224
                        status = true;
225
                    }
226 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_CHECKSUM)) {
227 6272 leinfelder
                    logMetacat.debug("Using resource 'checksum'");
228 6264 leinfelder
                    // handle checksum requests
229
                    if (httpVerb == GET) {
230 6514 leinfelder
                    	// after the command
231
                        extra = parseTrailing(resource, RESOURCE_CHECKSUM);
232 6284 leinfelder
                        checksum(extra);
233 6264 leinfelder
                        status = true;
234
                    }
235
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
236
                    // there are various parts to monitoring
237
                    if (httpVerb == GET) {
238 6514 leinfelder
                    	// after the command
239
                        extra = parseTrailing(resource, RESOURCE_MONITOR);
240 6803 leinfelder
241
                        // ping
242
                        if (extra.toLowerCase().equals("ping")) {
243
                            logMetacat.debug("processing ping request");
244
                            Date result = MNodeService.getInstance(request).ping();
245
                            // TODO: send to output
246
                            status = true;
247
248
                        } else {
249
	                    	// health monitoring calls
250
	                        status = monitor(extra);
251
                        }
252
253 6264 leinfelder
                    }
254 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
255 6264 leinfelder
                	if (httpVerb == POST) {
256 6272 leinfelder
	                    logMetacat.debug("processing replicate request");
257 6264 leinfelder
	                    replicate();
258
	                    status = true;
259
                	}
260 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_ERROR)) {
261 6264 leinfelder
	                // sync error
262
	                if (httpVerb == POST) {
263
	                    syncError();
264
	                    status = true;
265
	                }
266 6604 cjones
                } else if (resource.startsWith(RESOURCE_META_CHANGED)) {
267
                    // system metadata changed
268
                    if (httpVerb == POST) {
269 6650 leinfelder
                        systemMetadataChanged();
270 6604 cjones
                        status = true;
271
                    }
272
                } else if (resource.startsWith(RESOURCE_REPLICAS)) {
273
                    // get replica
274
                    if (httpVerb == GET) {
275
                        extra = parseTrailing(resource, RESOURCE_REPLICAS);
276
                        getReplica(extra);
277
                        status = true;
278
                    }
279 6264 leinfelder
                }
280
281
                if (!status) {
282 6273 leinfelder
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
283 6264 leinfelder
                }
284
            } else {
285 6273 leinfelder
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
286 6264 leinfelder
            }
287
        } catch (BaseException be) {
288
        	// report Exceptions as clearly as possible
289
        	OutputStream out = null;
290
			try {
291
				out = response.getOutputStream();
292
			} catch (IOException e) {
293
				logMetacat.error("Could not get output stream from response", e);
294
			}
295
            serializeException(be, out);
296
        } catch (Exception e) {
297 6273 leinfelder
            // report Exceptions as clearly and generically as possible
298
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
299
        	OutputStream out = null;
300
			try {
301
				out = response.getOutputStream();
302
			} catch (IOException ioe) {
303
				logMetacat.error("Could not get output stream from response", ioe);
304
			}
305
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
306
            serializeException(se, out);
307 6264 leinfelder
        }
308
    }
309
310 6604 cjones
311 6273 leinfelder
    /**
312 6604 cjones
     * Handles notification of system metadata changes for the given identifier
313
     *
314
     * @param id  the identifier for the object
315
     * @throws InvalidToken
316
     * @throws InvalidRequest
317
     * @throws NotAuthorized
318
     * @throws ServiceFailure
319
     * @throws NotImplemented
320
     */
321 6650 leinfelder
    private void systemMetadataChanged()
322 6604 cjones
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
323
        InvalidToken {
324
325
        long serialVersion = 0L;
326
        String serialVersionStr = null;
327
        Date dateSysMetaLastModified = null;
328
        String dateSysMetaLastModifiedStr = null;
329 6650 leinfelder
        Identifier pid = null;
330 6604 cjones
331 6660 leinfelder
        // mkae sure we have the multipart params
332
        try {
333
			initMultipartParams();
334
		} catch (Exception e1) {
335
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
336
		}
337
338 6650 leinfelder
        // get the pid
339
        try {
340 6660 leinfelder
        	String id = multipartparams.get("pid").get(0);
341 6650 leinfelder
        	pid = new Identifier();
342
            pid.setValue(id);
343
        } catch (NullPointerException e) {
344
            String msg = "The 'pid' must be provided as a parameter and was not.";
345
            logMetacat.error(msg);
346
            throw new InvalidRequest("1334", msg);
347
        }
348
349 6604 cjones
        // get the serialVersion
350
        try {
351 6660 leinfelder
            serialVersionStr = multipartparams.get("serialVersion").get(0);
352 6604 cjones
            serialVersion = new Long(serialVersionStr).longValue();
353
354
        } catch (NullPointerException e) {
355
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
356
            logMetacat.error(msg);
357
            throw new InvalidRequest("1334", msg);
358
359
        }
360
361
        // get the dateSysMetaLastModified
362
        try {
363 6660 leinfelder
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
364 6604 cjones
            dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
365
366
        } catch (NullPointerException e) {
367
            String msg =
368
                "The 'dateSysMetaLastModified' must be provided as a " +
369
                "parameter and was not, or was an invalid representation of the timestamp.";
370
            logMetacat.error(msg);
371
            throw new InvalidRequest("1334", msg);
372
373
        }
374
375
        // call the service
376
        MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
377
        response.setStatus(200);
378
    }
379
380
    /**
381 6273 leinfelder
     * Checks the access policy
382
     * @param id
383
     * @return
384
     * @throws ServiceFailure
385
     * @throws InvalidToken
386
     * @throws NotFound
387
     * @throws NotAuthorized
388
     * @throws NotImplemented
389
     * @throws InvalidRequest
390
     */
391 6264 leinfelder
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
392
		Identifier pid = new Identifier();
393
		pid.setValue(id);
394
		Permission permission = null;
395
		try {
396 6509 leinfelder
			String perm = params.get("action")[0];
397 6373 leinfelder
			permission = Permission.convert(perm);
398 6264 leinfelder
		} catch (Exception e) {
399
			logMetacat.warn("No permission specified");
400
		}
401 6542 leinfelder
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
402 6264 leinfelder
		response.setStatus(200);
403
		response.setContentType("text/xml");
404
		return result;
405
    }
406
407 6273 leinfelder
    /**
408
     * Processes failed synchronization message
409
     * @throws NotImplemented
410
     * @throws ServiceFailure
411
     * @throws NotAuthorized
412
     * @throws InvalidRequest
413
     * @throws JiBXException
414 6367 leinfelder
     * @throws IllegalAccessException
415
     * @throws InstantiationException
416
     * @throws IOException
417 6273 leinfelder
     */
418 6367 leinfelder
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
419 6264 leinfelder
    	SynchronizationFailed syncFailed = null;
420 6472 leinfelder
		try {
421
			syncFailed = collectSynchronizationFailed();
422
		} catch (ParserConfigurationException e) {
423
			throw new ServiceFailure("2161", e.getMessage());
424
		} catch (SAXException e) {
425
			throw new ServiceFailure("2161", e.getMessage());
426
		}
427
428 6542 leinfelder
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
429 6264 leinfelder
    }
430
431 6472 leinfelder
432 6273 leinfelder
    /**
433
     * Handles the monitoring resources
434
     * @return
435
     * @throws NotFound
436
     * @throws ParseException
437
     * @throws NotImplemented
438
     * @throws ServiceFailure
439
     * @throws NotAuthorized
440
     * @throws InvalidRequest
441
     * @throws InsufficientResources
442
     * @throws UnsupportedType
443
     * @throws IOException
444
     * @throws JiBXException
445
     */
446 6297 cjones
    private boolean monitor(String pathInfo)
447
      throws NotFound, ParseException, NotImplemented, ServiceFailure,
448
      NotAuthorized, InvalidRequest, InsufficientResources, UnsupportedType,
449
      IOException, JiBXException {
450 6272 leinfelder
    	logMetacat.debug("processing monitor request");
451 6264 leinfelder
452 6272 leinfelder
        logMetacat.debug("verb is GET");
453
        logMetacat.debug("pathInfo is " + pathInfo);
454 6264 leinfelder
455 6803 leinfelder
        if (pathInfo.toLowerCase().equals("status")) {
456 6272 leinfelder
            logMetacat.debug("processing status request");
457 6264 leinfelder
            // TODO: implement in MNCore
458
            //MNodeService.getInstance().getStatus();
459
            return false;
460
461
        } else if (pathInfo.toLowerCase().equals("object")) {
462 6272 leinfelder
            logMetacat.debug("processing object request");
463 6264 leinfelder
            Identifier pid = null;
464
            ObjectFormat format = null;
465
            if (params.containsKey("format")) {
466
                String f = params.get("format")[0];
467
                format = ObjectFormatCache.getInstance().getFormat(f);
468
            }
469
            if (params.containsKey("pid")) {
470
                String id = params.get("pid")[0];
471
                pid = new Identifier();
472
                pid.setValue(id);
473
            }
474
475
            // TODO: implement in MNCore
476
            //ObjectStatistics objectStats = MNodeService.getInstance().getObjectStatistics(format, pid);
477 6274 leinfelder
            return false;
478 6264 leinfelder
479
        } else if (pathInfo.toLowerCase().equals("event")) {
480 6272 leinfelder
            logMetacat.debug("processing event request");
481 6297 cjones
            ObjectFormatIdentifier fmtid = null;
482
            String fromDateStr = null;
483
            Date fromDate = null;
484
            String toDateStr = null;
485
            Date toDate = null;
486 6264 leinfelder
            String requestor = null;
487
            Subject subject = null;
488
            String eventName = null;
489
            Event event = null;
490
491 6297 cjones
            if (params.containsKey("formatId")) {
492
                String f = params.get("formatId")[0];
493 6561 leinfelder
                fmtid = ObjectFormatCache.getInstance().getFormat(f).getFormatId();
494 6264 leinfelder
            }
495 6297 cjones
496
            if (params.containsKey("fromDate")) {
497
                fromDateStr = params.get("fromDate")[0];
498
                fromDate = getDateAsUTC(fromDateStr);
499 6264 leinfelder
            }
500 6297 cjones
501
            if (params.containsKey("toDate")) {
502
              toDateStr = params.get("toDate")[0];
503
              toDate = getDateAsUTC(toDateStr);
504
            }
505
506 6264 leinfelder
            if (params.containsKey("requestor")) {
507
            	requestor = params.get("requestor")[0];
508
            	subject = new Subject();
509
            	subject.setValue(requestor);
510
            }
511 6297 cjones
512 6264 leinfelder
            if (params.containsKey("event")) {
513
            	eventName = params.get("event")[0];
514 6373 leinfelder
                event = Event.convert(eventName);
515 6264 leinfelder
            }
516
517 6542 leinfelder
            MonitorList monitorList = MNodeService.getInstance(request).getOperationStatistics(session, fromDate, toDate, subject, event, fmtid);
518 6264 leinfelder
519
            OutputStream out = response.getOutputStream();
520
            response.setStatus(200);
521
            response.setContentType("text/xml");
522
523 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(monitorList, out);
524 6264 leinfelder
525 6274 leinfelder
            return true;
526
527 6264 leinfelder
        }
528
529 6274 leinfelder
        return false;
530 6264 leinfelder
    }
531
532 6297 cjones
    /*
533
     * Parse an ISO8601 date string, returning a Date in UTC time if the string
534
     * ends in 'Z'.
535
     *
536
     * @param fromDateStr -  the date string to be parsed
537
     * @return date -  the date object represented by the string
538
     */
539
    private Date getDateAsUTC(String fromDateStr)
540
      throws ParseException {
541
542
    	Date date = null;
543
544
    	try {
545
    		// try the expected date format
546
        // a date format for date string arguments
547
        DateFormat dateFormat =
548
        	new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
549
550
	      date = dateFormat.parse(fromDateStr);
551
552
    	} catch (ParseException e) {
553
    		// try the date with the UTC indicator
554
        DateFormat utcDateFormat =
555
        	new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
556
        utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
557
        date = utcDateFormat.parse(fromDateStr);
558
559
      }
560
561
    	return date;
562
    }
563
564
		/**
565 6273 leinfelder
     * Calculate the checksum
566
     * @throws NotImplemented
567
     * @throws JiBXException
568
     * @throws IOException
569
     * @throws InvalidToken
570
     * @throws ServiceFailure
571
     * @throws NotAuthorized
572
     * @throws NotFound
573
     * @throws InvalidRequest
574
     */
575 6285 cjones
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
576 6264 leinfelder
    	String checksumAlgorithm = "MD5";
577 6284 leinfelder
578 6285 cjones
        Identifier pidid = new Identifier();
579
        pidid.setValue(pid);
580 6264 leinfelder
        try {
581
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
582
        } catch(Exception e) {
583
            //do nothing.  default to MD5
584
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
585
        }
586 6285 cjones
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
587 6273 leinfelder
588 6542 leinfelder
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
589 6273 leinfelder
        logMetacat.debug("got checksum " + c.getValue());
590
        response.setStatus(200);
591
        logMetacat.debug("serializing response");
592 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
593 6273 leinfelder
        logMetacat.debug("done serializing response.");
594
595 6264 leinfelder
    }
596
597
	/**
598
     * handle the replicate action for MN
599 6273 leinfelder
	 * @throws JiBXException
600
	 * @throws FileUploadException
601
	 * @throws IOException
602
	 * @throws InvalidRequest
603
	 * @throws ServiceFailure
604
	 * @throws UnsupportedType
605
	 * @throws InsufficientResources
606
	 * @throws NotAuthorized
607
	 * @throws NotImplemented
608 6367 leinfelder
	 * @throws IllegalAccessException
609
	 * @throws InstantiationException
610 6264 leinfelder
     */
611 6678 cjones
    private void replicate()
612
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException,
613
        JiBXException, NotImplemented, NotAuthorized, InsufficientResources,
614
        UnsupportedType, InstantiationException, IllegalAccessException {
615 6264 leinfelder
616 6272 leinfelder
        logMetacat.debug("in POST replicate()");
617 6264 leinfelder
618 6273 leinfelder
        //parse the systemMetadata
619 6679 leinfelder
        final SystemMetadata sysmeta = collectSystemMetadata();
620 6264 leinfelder
621 6273 leinfelder
        String sn = multipartparams.get("sourceNode").get(0);
622 6272 leinfelder
        logMetacat.debug("sourceNode: " + sn);
623 6679 leinfelder
        final NodeReference sourceNode = new NodeReference();
624 6548 cjones
        sourceNode.setValue(sn);
625 6679 leinfelder
626
        // run it in a thread to avoid connection timeout
627
        Runnable runner = new Runnable() {
628
			@Override
629
			public void run() {
630
				try {
631
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
632
				} catch (Exception e) {
633
					throw new RuntimeException(e.getMessage(), e);
634
				}
635
			}
636
    	};
637
    	ExecutorService executor = Executors.newSingleThreadExecutor();
638
    	executor.execute(runner);
639
    	executor.shutdown();
640
641
    	// thread was started, so we return success
642 6678 cjones
        response.setStatus(200);
643 6679 leinfelder
644 6264 leinfelder
    }
645 6604 cjones
646 6264 leinfelder
    /**
647 6604 cjones
     * Handle the getReplica action for the MN
648
     * @param id  the identifier for the object
649
     * @throws NotFound
650
     * @throws ServiceFailure
651
     * @throws NotImplemented
652
     * @throws NotAuthorized
653
     * @throws InvalidToken
654
     * @throws InvalidRequest
655
     */
656
    private void getReplica(String id)
657
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
658
        ServiceFailure, NotFound {
659
660
        Identifier pid = new Identifier();
661
        pid.setValue(id);
662
        OutputStream out = null;
663
        InputStream dataBytes = null;
664
665
        try {
666
            // call the service
667
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
668
669
            response.setContentType("application/octet-stream");
670
            response.setStatus(200);
671
            out = response.getOutputStream();
672
            // write the object to the output stream
673
            IOUtils.copyLarge(dataBytes, out);
674
675
        } catch (IOException e) {
676
            String msg = "There was an error writing the output: " + e.getMessage();
677
            logMetacat.error(msg);
678
            throw new ServiceFailure("2181", msg);
679
680
        }
681
682
    }
683
684
    /**
685 6264 leinfelder
     * Get the Node information
686
     *
687
     * @throws JiBXException
688
     * @throws IOException
689
     * @throws InvalidRequest
690
     * @throws ServiceFailure
691
     * @throws NotAuthorized
692
     * @throws NotImplemented
693
     */
694
    private void node()
695
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
696
697 6542 leinfelder
        Node n = MNodeService.getInstance(request).getCapabilities();
698 6264 leinfelder
699
        response.setContentType("text/xml");
700
        response.setStatus(200);
701 6501 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
702 6264 leinfelder
703
    }
704
705
    /**
706
     * MN_crud.describe()
707
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
708 6285 cjones
     * @param pid
709 6273 leinfelder
     * @throws InvalidRequest
710
     * @throws NotImplemented
711
     * @throws NotFound
712
     * @throws NotAuthorized
713
     * @throws ServiceFailure
714
     * @throws InvalidToken
715 6264 leinfelder
     */
716 6285 cjones
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
717 6264 leinfelder
    {
718
        response.setStatus(200);
719
        response.setContentType("text/xml");
720 6273 leinfelder
721 6264 leinfelder
        Identifier id = new Identifier();
722 6285 cjones
        id.setValue(pid);
723 6273 leinfelder
724 6542 leinfelder
        DescribeResponse dr = MNodeService.getInstance(request).describe(session, id);
725 6502 leinfelder
        //response.addHeader("pid", pid);
726 6507 leinfelder
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
727 6502 leinfelder
        response.addHeader("Content-Length", dr.getContent_Length() + "");
728
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
729 6691 leinfelder
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
730 6773 leinfelder
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
731
732 6502 leinfelder
733 6264 leinfelder
    }
734
735
    /**
736
     * get the logs based on passed params.  Available
737
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
738
     * for more info
739 6273 leinfelder
     * @throws NotImplemented
740
     * @throws InvalidRequest
741
     * @throws NotAuthorized
742
     * @throws ServiceFailure
743
     * @throws InvalidToken
744
     * @throws IOException
745
     * @throws JiBXException
746 6264 leinfelder
     */
747 6273 leinfelder
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
748 6264 leinfelder
    {
749 6273 leinfelder
750
        Date fromDate = null;
751
        Date toDate = null;
752
        Event event = null;
753
        Integer start = null;
754
        Integer count = null;
755
756 6264 leinfelder
        try {
757 6273 leinfelder
        	String fromDateS = params.get("fromDate")[0];
758
            logMetacat.debug("param fromDateS: " + fromDateS);
759 6469 leinfelder
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
760 6273 leinfelder
        } catch (Exception e) {
761
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
762 6264 leinfelder
        }
763 6273 leinfelder
        try {
764
        	String toDateS = params.get("toDate")[0];
765
            logMetacat.debug("param toDateS: " + toDateS);
766 6469 leinfelder
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
767 6273 leinfelder
        } catch (Exception e) {
768
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
769
		}
770
        try {
771
        	String eventS = params.get("event")[0];
772
            event = Event.convert(eventS);
773
        } catch (Exception e) {
774
        	logMetacat.warn("Could not parse event: " + e.getMessage());
775
		}
776
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
777
778
        try {
779
        	start =  Integer.parseInt(params.get("start")[0]);
780
        } catch (Exception e) {
781
			logMetacat.warn("Could not parse start: " + e.getMessage());
782
		}
783
        try {
784
        	count =  Integer.parseInt(params.get("count")[0]);
785
        } catch (Exception e) {
786
			logMetacat.warn("Could not parse count: " + e.getMessage());
787
		}
788
789
        logMetacat.debug("calling getLogRecords");
790 6542 leinfelder
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, start, count);
791 6273 leinfelder
792
        OutputStream out = response.getOutputStream();
793
        response.setStatus(200);
794
        response.setContentType("text/xml");
795
796 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(log, out);
797 6273 leinfelder
798 6264 leinfelder
    }
799
800
    /**
801
     * Implements REST version of DataONE CRUD API --> get
802 6285 cjones
     * @param pid ID of data object to be read
803 6273 leinfelder
     * @throws NotImplemented
804
     * @throws InvalidRequest
805
     * @throws NotFound
806
     * @throws NotAuthorized
807
     * @throws ServiceFailure
808
     * @throws InvalidToken
809
     * @throws IOException
810
     * @throws JiBXException
811 6264 leinfelder
     */
812 6285 cjones
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
813 6264 leinfelder
        OutputStream out = null;
814 6273 leinfelder
815 6285 cjones
        if (pid != null) { //get a specific document
816 6273 leinfelder
            Identifier id = new Identifier();
817 6285 cjones
            id.setValue(pid);
818 6273 leinfelder
819 6542 leinfelder
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
820 6264 leinfelder
821 6273 leinfelder
            //set the content type
822 6561 leinfelder
            if (sm.getFormatId().getValue().trim().equals(
823
            		ObjectFormatCache.getInstance().getFormat("text/csv").getFormatId().getValue()))
824 6273 leinfelder
            {
825
                response.setContentType("text/csv");
826
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".csv");
827
            }
828 6561 leinfelder
            else if (sm.getFormatId().getValue().trim().equals(
829
            		ObjectFormatCache.getInstance().getFormat("text/plain").getFormatId().getValue()))
830 6273 leinfelder
            {
831
                response.setContentType("text/plain");
832
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".txt");
833
            }
834 6561 leinfelder
            else if (sm.getFormatId().getValue().trim().equals(
835
            		ObjectFormatCache.getInstance().getFormat("application/octet-stream").getFormatId().getValue()))
836 6273 leinfelder
            {
837
                response.setContentType("application/octet-stream");
838
            }
839
            else
840
            {
841
                response.setContentType("text/xml");
842
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".xml");
843
            }
844
845 6542 leinfelder
            InputStream data = MNodeService.getInstance(request).get(session, id);
846 6273 leinfelder
847
            out = response.getOutputStream();
848
            IOUtils.copyLarge(data, out);
849
850
        }
851
        else
852
        { //call listObjects with specified params
853
            Date startTime = null;
854
            Date endTime = null;
855
            ObjectFormat objectFormat = null;
856
            boolean replicaStatus = false;
857
            int start = 0;
858
            //TODO: make the max count into a const
859
            int count = 1000;
860
            Enumeration paramlist = request.getParameterNames();
861
            while (paramlist.hasMoreElements())
862
            { //parse the params and make the crud call
863
                String name = (String) paramlist.nextElement();
864
                String[] value = (String[])request.getParameterValues(name);
865
866 6799 cjones
                if (name.equals("fromDate") && value != null)
867 6264 leinfelder
                {
868 6273 leinfelder
                    try
869 6264 leinfelder
                    {
870 6273 leinfelder
                      //startTime = dateFormat.parse(value[0]);
871 6469 leinfelder
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
872
                        //startTime = parseDateAndConvertToGMT(value[0]);
873 6264 leinfelder
                    }
874 6273 leinfelder
                    catch(Exception e)
875 6799 cjones
                    {  //if we can't parse it, just don't use the fromDate param
876
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
877 6273 leinfelder
                        startTime = null;
878 6264 leinfelder
                    }
879 6273 leinfelder
                }
880 6799 cjones
                else if(name.equals("toDate") && value != null)
881 6273 leinfelder
                {
882
                    try
883 6264 leinfelder
                    {
884 6469 leinfelder
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
885 6264 leinfelder
                    }
886 6273 leinfelder
                    catch(Exception e)
887 6799 cjones
                    {  //if we can't parse it, just don't use the toDate param
888
                        logMetacat.warn("Could not parse toDate: " + value[0]);
889 6273 leinfelder
                        endTime = null;
890
                    }
891 6264 leinfelder
                }
892 6273 leinfelder
                else if(name.equals("objectFormat") && value != null)
893 6264 leinfelder
                {
894 6273 leinfelder
                    objectFormat = ObjectFormatCache.getInstance().getFormat(value[0]);
895 6264 leinfelder
                }
896 6273 leinfelder
                else if(name.equals("replicaStatus") && value != null)
897 6264 leinfelder
                {
898 6273 leinfelder
                    if(value != null &&
899
                       value.length > 0 &&
900
                       (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
901
                    {
902
                        replicaStatus = true;
903
                    }
904 6264 leinfelder
                }
905 6273 leinfelder
                else if(name.equals("start") && value != null)
906 6264 leinfelder
                {
907 6273 leinfelder
                    start = new Integer(value[0]).intValue();
908 6264 leinfelder
                }
909 6273 leinfelder
                else if(name.equals("count") && value != null)
910 6264 leinfelder
                {
911 6273 leinfelder
                    count = new Integer(value[0]).intValue();
912 6264 leinfelder
                }
913
            }
914 6273 leinfelder
            //make the crud call
915
            logMetacat.debug("session: " + session + " startTime: " + startTime +
916
                    " endtime: " + endTime + " objectFormat: " +
917
                    objectFormat + " replicaStatus: " + replicaStatus +
918
                    " start: " + start + " count: " + count);
919
920 6432 leinfelder
            ObjectFormatIdentifier fmtid = null;
921 6352 cjones
922
            if ( objectFormat != null ) {
923 6561 leinfelder
          	 fmtid = objectFormat.getFormatId();
924 6352 cjones
925
            }
926 6273 leinfelder
            ObjectList ol =
927 6542 leinfelder
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime,
928 6352 cjones
               fmtid, replicaStatus, start, count);
929
930 6273 leinfelder
            out = response.getOutputStream();
931
            response.setStatus(200);
932
            response.setContentType("text/xml");
933
            // Serialize and write it to the output stream
934 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
935 6273 leinfelder
936 6264 leinfelder
        }
937 6273 leinfelder
938 6264 leinfelder
    }
939
940 6273 leinfelder
941 6264 leinfelder
    /**
942 6273 leinfelder
     * Retrieve System Metadata
943 6285 cjones
     * @param pid
944 6273 leinfelder
     * @throws InvalidToken
945
     * @throws ServiceFailure
946
     * @throws NotAuthorized
947
     * @throws NotFound
948
     * @throws InvalidRequest
949
     * @throws NotImplemented
950
     * @throws IOException
951
     * @throws JiBXException
952 6264 leinfelder
     */
953 6285 cjones
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
954 6273 leinfelder
955
        Identifier id = new Identifier();
956 6285 cjones
        id.setValue(pid);
957 6542 leinfelder
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
958 6273 leinfelder
959
        response.setContentType("text/xml");
960
        response.setStatus(200);
961
        OutputStream out = response.getOutputStream();
962
963
        // Serialize and write it to the output stream
964 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
965 6273 leinfelder
   }
966 6264 leinfelder
967
968
    /**
969
     * Inserts or updates the object
970
     *
971 6285 cjones
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
972 6264 leinfelder
     *               is the existing pid.  If insert, the pid is the new one
973 6269 leinfelder
     * @throws InvalidRequest
974
     * @throws ServiceFailure
975
     * @throws JiBXException
976
     * @throws NotImplemented
977
     * @throws InvalidSystemMetadata
978
     * @throws InsufficientResources
979
     * @throws UnsupportedType
980
     * @throws IdentifierNotUnique
981
     * @throws NotAuthorized
982
     * @throws InvalidToken
983
     * @throws NotFound
984 6273 leinfelder
     * @throws IOException
985 6367 leinfelder
     * @throws IllegalAccessException
986
     * @throws InstantiationException
987 6264 leinfelder
     */
988 6367 leinfelder
    protected void putObject(String pid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
989 6272 leinfelder
        logMetacat.debug("putObject with pid " + pid);
990 6264 leinfelder
        logMetacat.debug("Entering putObject: " + pid + "/" + action);
991 6269 leinfelder
992 6273 leinfelder
        response.setStatus(200);
993
        response.setContentType("text/xml");
994
        OutputStream out = response.getOutputStream();
995
996 6269 leinfelder
        // Read the incoming data from its Mime Multipart encoding
997
    	Map<String, File> files = collectMultipartFiles();
998
        InputStream object = null;
999
        InputStream sysmeta = null;
1000 6264 leinfelder
1001 6269 leinfelder
        File smFile = files.get("sysmeta");
1002
        sysmeta = new FileInputStream(smFile);
1003
        File objFile = files.get("object");
1004
        object = new FileInputStream(objFile);
1005
1006
        if (action.equals(FUNCTION_NAME_INSERT)) {
1007
            // handle inserts
1008
            logMetacat.debug("Commence creation...");
1009 6367 leinfelder
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1010 6264 leinfelder
1011 6269 leinfelder
            Identifier id = new Identifier();
1012
            id.setValue(pid);
1013 6272 leinfelder
            logMetacat.debug("creating object with pid " + pid);
1014 6542 leinfelder
            Identifier rId = MNodeService.getInstance(request).create(session, id, object, smd);
1015 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1016 6264 leinfelder
1017 6269 leinfelder
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1018
        	// handle updates
1019
1020
            // construct pids
1021
            Identifier newPid = new Identifier();
1022
            try {
1023
            	String newPidString = multipartparams.get("newPid").get(0);
1024 6264 leinfelder
                newPid.setValue(newPidString);
1025 6269 leinfelder
            } catch (Exception e) {
1026
				logMetacat.warn("newPid not given");
1027
			}
1028
1029
            Identifier obsoletedPid = new Identifier();
1030
            obsoletedPid.setValue(pid);
1031
1032
            logMetacat.debug("Commence update...");
1033
1034
            // get the systemmetadata object
1035 6367 leinfelder
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1036 6264 leinfelder
1037 6542 leinfelder
            Identifier rId = MNodeService.getInstance(request).update(session, obsoletedPid, object, newPid, smd);
1038 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1039 6269 leinfelder
        } else {
1040
            throw new InvalidRequest("1000", "Operation must be create or update.");
1041
        }
1042 6264 leinfelder
1043 6269 leinfelder
1044 6264 leinfelder
    }
1045
1046
    /**
1047
     * Handle delete
1048 6285 cjones
     * @param pid ID of data object to be deleted
1049 6264 leinfelder
     * @throws IOException
1050 6273 leinfelder
     * @throws InvalidRequest
1051
     * @throws NotImplemented
1052
     * @throws NotFound
1053
     * @throws NotAuthorized
1054
     * @throws ServiceFailure
1055
     * @throws InvalidToken
1056
     * @throws JiBXException
1057 6264 leinfelder
     */
1058 6285 cjones
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException
1059 6264 leinfelder
    {
1060
1061
        OutputStream out = response.getOutputStream();
1062
        response.setStatus(200);
1063
        response.setContentType("text/xml");
1064
1065
        Identifier id = new Identifier();
1066 6285 cjones
        id.setValue(pid);
1067 6273 leinfelder
1068
        logMetacat.debug("Calling delete");
1069 6542 leinfelder
        MNodeService.getInstance(request).delete(session, id);
1070 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1071 6273 leinfelder
1072
    }
1073 6264 leinfelder
1074 6514 leinfelder
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1075
1076
		// Read the incoming data from its Mime Multipart encoding
1077
		logMetacat.debug("Disassembling MIME multipart form");
1078
		InputStream sf = null;
1079
1080
		// handle MMP inputs
1081
		File tmpDir = getTempDirectory();
1082
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1083
		MultipartRequestResolver mrr =
1084
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1085
		MultipartRequest mr = null;
1086
		try {
1087
			mr = mrr.resolveMultipart(request);
1088
		} catch (Exception e) {
1089
			throw new ServiceFailure("2161",
1090
					"Could not resolve multipart: " + e.getMessage());
1091
		}
1092
		logMetacat.debug("resolved multipart request");
1093
		Map<String, File> files = mr.getMultipartFiles();
1094
		if (files == null || files.keySet() == null) {
1095
			throw new InvalidRequest("2163",
1096
					"must have multipart file with name 'message'");
1097
		}
1098
		logMetacat.debug("got multipart files");
1099
1100
		multipartparams = mr.getMultipartParameters();
1101
1102
		File sfFile = files.get("message");
1103
		if (sfFile == null) {
1104
			throw new InvalidRequest("2163",
1105
					"Missing the required file-part 'message' from the multipart request.");
1106
		}
1107
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1108
		sf = new FileInputStream(sfFile);
1109
1110
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1111
		return syncFailed;
1112
	}
1113
1114 6264 leinfelder
}