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: leinfelder $'
7
 *     '$Date: 2013-05-23 10:20:40 -0700 (Thu, 23 May 2013) $'
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
import java.util.TimeZone;
37
import java.util.concurrent.ExecutorService;
38
import java.util.concurrent.Executors;
39

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

    
45
import org.apache.commons.fileupload.FileUploadException;
46
import org.apache.commons.io.IOUtils;
47
import org.apache.log4j.Logger;
48
import org.dataone.client.ObjectFormatCache;
49
import org.dataone.client.formats.ObjectFormatInfo;
50
import org.dataone.mimemultipart.MultipartRequest;
51
import org.dataone.mimemultipart.MultipartRequestResolver;
52
import org.dataone.service.exceptions.BaseException;
53
import org.dataone.service.exceptions.IdentifierNotUnique;
54
import org.dataone.service.exceptions.InsufficientResources;
55
import org.dataone.service.exceptions.InvalidRequest;
56
import org.dataone.service.exceptions.InvalidSystemMetadata;
57
import org.dataone.service.exceptions.InvalidToken;
58
import org.dataone.service.exceptions.NotAuthorized;
59
import org.dataone.service.exceptions.NotFound;
60
import org.dataone.service.exceptions.NotImplemented;
61
import org.dataone.service.exceptions.ServiceFailure;
62
import org.dataone.service.exceptions.SynchronizationFailed;
63
import org.dataone.service.exceptions.UnsupportedType;
64
import org.dataone.service.types.v1.Checksum;
65
import org.dataone.service.types.v1.DescribeResponse;
66
import org.dataone.service.types.v1.Event;
67
import org.dataone.service.types.v1.Identifier;
68
import org.dataone.service.types.v1.Log;
69
import org.dataone.service.types.v1.MonitorList;
70
import org.dataone.service.types.v1.Node;
71
import org.dataone.service.types.v1.NodeReference;
72
import org.dataone.service.types.v1.ObjectFormat;
73
import org.dataone.service.types.v1.ObjectFormatIdentifier;
74
import org.dataone.service.types.v1.ObjectList;
75
import org.dataone.service.types.v1.Permission;
76
import org.dataone.service.types.v1.Subject;
77
import org.dataone.service.types.v1.SystemMetadata;
78
import org.dataone.service.types.v1_1.QueryEngineDescription;
79
import org.dataone.service.types.v1_1.QueryEngineList;
80
import org.dataone.service.util.Constants;
81
import org.dataone.service.util.DateTimeMarshaller;
82
import org.dataone.service.util.ExceptionHandler;
83
import org.dataone.service.util.TypeMarshaller;
84
import org.jibx.runtime.JiBXException;
85
import org.xml.sax.SAXException;
86

    
87
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeInputStream;
88
import edu.ucsb.nceas.metacat.dataone.MNodeService;
89
import edu.ucsb.nceas.metacat.properties.PropertyService;
90
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
91

    
92
/**
93
 * MN REST service implementation handler
94
 * 
95
 * ******************
96
 * MNCore -- DONE
97
 * 		ping() - GET /d1/mn/monitor/ping
98
 * 		log() - GET /d1/mn/log
99
 * 		**getObjectStatistics() - GET /d1/mn/monitor/object
100
 * 		getOperationsStatistics - GET /d1/mn/monitor/event
101
 * 		**getStatus - GET /d1/mn/monitor/status
102
 * 		getCapabilities() - GET /d1/mn/ and /d1/mn/node
103
 * 	
104
 * 	MNRead -- DONE
105
 * 		get() - GET /d1/mn/object/PID
106
 * 		getSystemMetadata() - GET /d1/mn/meta/PID
107
 * 		describe() - HEAD /d1/mn/object/PID
108
 * 		getChecksum() - GET /d1/mn/checksum/PID
109
 * 		listObjects() - GET /d1/mn/object
110
 * 		synchronizationFailed() - POST /d1/mn/error
111
 * 	
112
 * 	MNAuthorization -- DONE
113
 * 		isAuthorized() - GET /d1/mn/isAuthorized/PID
114
 * 		setAccessPolicy() - PUT /d1/mn/accessRules/PID
115
 * 		
116
 * 	MNStorage - DONE
117
 * 		create() - POST /d1/mn/object/PID
118
 * 		update() - PUT /d1/mn/object/PID
119
 * 		delete() - DELETE /d1/mn/object/PID
120
 * 		archive() - PUT /d1/mn/archive/PID
121

    
122
 *    systemMetadataChanged() - POST /dirtySystemMetadata/PID
123
 * 	
124
 * 	MNReplication
125
 * 		replicate() - POST /d1/mn/replicate
126
 *    getReplica() - GET /d1/mn/replica
127
 * 
128
 * ******************
129
 * @author leinfelder
130
 *
131
 */
132
public class MNResourceHandler extends D1ResourceHandler {
133

    
134
    // MN-specific API Resources
135
    protected static final String RESOURCE_MONITOR = "monitor";
136
    protected static final String RESOURCE_REPLICATE = "replicate";
137
    protected static final String RESOURCE_REPLICAS = "replica";
138
    protected static final String RESOURCE_NODE = "node";
139
    protected static final String RESOURCE_ERROR = "error";
140
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
141
    protected static final String RESOURCE_GENERATE_ID = "generate";
142

    
143
    
144
    // shared executor
145
	private static ExecutorService executor = null;
146

    
147
	static {
148
		// use a shared executor service with nThreads == one less than available processors
149
    	int availableProcessors = Runtime.getRuntime().availableProcessors();
150
        int nThreads = availableProcessors * 1;
151
        nThreads--;
152
        nThreads = Math.max(1, nThreads);
153
    	executor = Executors.newFixedThreadPool(nThreads);	
154
	}
155
	
156
    /**
157
     * Initializes new instance by setting servlet context,request and response
158
     * */
159
    public MNResourceHandler(ServletContext servletContext,
160
            HttpServletRequest request, HttpServletResponse response) {
161
    	super(servletContext, request, response);
162
        logMetacat = Logger.getLogger(MNResourceHandler.class);
163
    }
164
    
165
    @Override
166
    protected boolean isD1Enabled() {
167
    	
168
    	boolean enabled = false;
169
    	try {
170
			enabled = Boolean.parseBoolean(PropertyService.getProperty("dataone.mn.services.enabled"));
171
		} catch (PropertyNotFoundException e) {
172
			logMetacat.error("Could not check if DataONE is enabled: " + e.getMessage());
173
		}
174
    	
175
    	return enabled;	
176
    }
177

    
178
    /**
179
     * This function is called from REST API servlet and handles each request to the servlet 
180
     * 
181
     * @param httpVerb (GET, POST, PUT or DELETE)
182
     */
183
    @Override
184
    public void handle(byte httpVerb) {
185
    	// prepare the handler
186
    	super.handle(httpVerb);
187
    	
188
        try {
189
        	
190
        	// only service requests if we have D1 configured
191
        	if (!isD1Enabled()) {
192
        		ServiceFailure se = new ServiceFailure("0000", "DataONE services are not enabled on this node");
193
                serializeException(se, response.getOutputStream());
194
                return;
195
        	}
196
        	
197
        	// get the resource
198
            String resource = request.getPathInfo();
199
            resource = resource.substring(resource.indexOf("/") + 1);
200
            
201
            // default to node info
202
            if (resource.equals("")) {
203
                resource = RESOURCE_NODE;
204
            }
205
            
206
            // get the rest of the path info
207
            String extra = null;
208
                        
209
            logMetacat.debug("handling verb " + httpVerb + " request with resource '" + resource + "'");
210
            logMetacat.debug("resource: '" + resource + "'");
211
            boolean status = false;
212
            
213
            if (resource != null) {
214

    
215
                if (resource.startsWith(RESOURCE_NODE)) {
216
                    // node response
217
                    node();
218
                    status = true;
219
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
220
                    if (httpVerb == GET) {
221
                    	// after the command
222
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
223
	                	// check the access rules
224
	                    isAuthorized(extra);
225
	                    status = true;
226
	                    logMetacat.debug("done getting access");
227
                    }
228
                } else if (resource.startsWith(RESOURCE_META)) {
229
                    logMetacat.debug("Using resource 'meta'");
230
                    // get
231
                    if (httpVerb == GET) {
232
                    	// after the command
233
                        extra = parseTrailing(resource, RESOURCE_META);
234
                        getSystemMetadataObject(extra);
235
                        status = true;
236
                    }
237
                    
238
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
239
                    logMetacat.debug("Using resource 'object'");
240
                    // after the command
241
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
242
                    logMetacat.debug("objectId: " + extra);
243
                    logMetacat.debug("verb:" + httpVerb);
244

    
245
                    if (httpVerb == GET) {
246
                        getObject(extra);
247
                        status = true;
248
                    } else if (httpVerb == POST) {
249
                    	// part of the params, not the URL
250
                        putObject(null, FUNCTION_NAME_INSERT);
251
                        status = true;
252
                    } else if (httpVerb == PUT) {
253
                        putObject(extra, FUNCTION_NAME_UPDATE);
254
                        status = true;
255
                    } else if (httpVerb == DELETE) {
256
                        deleteObject(extra);
257
                        status = true;
258
                    } else if (httpVerb == HEAD) {
259
                        describeObject(extra);
260
                        status = true;
261
                    }
262
                  
263
                } else if (resource.startsWith(RESOURCE_LOG)) {
264
                    logMetacat.debug("Using resource 'log'");
265
                    // handle log events
266
                    if (httpVerb == GET) {
267
                        getLog();
268
                        status = true;
269
                    }
270
                } else if (resource.startsWith(Constants.RESOURCE_ARCHIVE)) {
271
                    logMetacat.debug("Using resource " + Constants.RESOURCE_ARCHIVE);
272
                    // handle archive events
273
                    if (httpVerb == PUT) {
274
                        extra = parseTrailing(resource, Constants.RESOURCE_ARCHIVE);
275
                        archive(extra);
276
                        status = true;
277
                    }
278
                } else if (resource.startsWith(Constants.RESOURCE_CHECKSUM)) {
279
                    logMetacat.debug("Using resource 'checksum'");
280
                    // handle checksum requests
281
                    if (httpVerb == GET) {
282
                    	// after the command
283
                        extra = parseTrailing(resource, Constants.RESOURCE_CHECKSUM);
284
                        checksum(extra);
285
                        status = true;
286
                    }
287
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
288
                    // there are various parts to monitoring
289
                    if (httpVerb == GET) {
290
                    	// after the command
291
                        extra = parseTrailing(resource, RESOURCE_MONITOR);
292
                        
293
                        // ping
294
                        if (extra.toLowerCase().equals("ping")) {
295
                            logMetacat.debug("processing ping request");
296
                            Date result = MNodeService.getInstance(request).ping();
297
                            // TODO: send to output	
298
                            status = true;
299
                            
300
                        } else {
301
	                    	// health monitoring calls
302
	                        status = monitor(extra);
303
                        }
304
                        
305
                    }
306
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
307
                	if (httpVerb == POST) {
308
	                    logMetacat.debug("processing replicate request");
309
	                    replicate();
310
	                    status = true;
311
                	}
312
                } else if (resource.startsWith(RESOURCE_ERROR)) {
313
	                // sync error
314
	                if (httpVerb == POST) {
315
	                    syncError();
316
	                    status = true;
317
	                }
318
                } else if (resource.startsWith(RESOURCE_META_CHANGED)) {
319
                    // system metadata changed
320
                    if (httpVerb == POST) {
321
                        systemMetadataChanged();
322
                        status = true;
323
                    }
324
                } else if (resource.startsWith(RESOURCE_REPLICAS)) {
325
                    // get replica
326
                    if (httpVerb == GET) {
327
                        extra = parseTrailing(resource, RESOURCE_REPLICAS);
328
                        getReplica(extra);
329
                        status = true;
330
                    }
331
                } else if (resource.startsWith(RESOURCE_QUERY)) {
332
	                logMetacat.debug("Using resource " + RESOURCE_QUERY);
333
	                // after the command
334
	                extra = parseTrailing(resource, RESOURCE_QUERY);
335
	                logMetacat.debug("query extra: " + extra);
336

    
337
	                String engine = null;
338
	                String query = null;
339

    
340
	                if (extra != null) {
341
		                // get the engine
342
		                int engineIndex = extra.length();
343
		                if (extra.indexOf("/") > -1) {
344
		                	engineIndex = extra.indexOf("/");
345
		                }
346
		                engine = extra.substring(0, engineIndex);
347
		                logMetacat.debug("query engine: " + engine);
348
		                
349
		                // get the query if it is there
350
		                query = extra.substring(engineIndex, extra.length());
351
		                if (query != null && query.length() == 0) {
352
		                	query = null;
353
		                } else {
354
		                	if (query.startsWith("/")) {
355
		                		query = query.substring(1);
356
		                    }
357
		                }
358
		                logMetacat.debug("query: " + query);
359

    
360
	                }
361
	                logMetacat.debug("verb:" + httpVerb);
362
	                if (httpVerb == GET) {
363
	                    doQuery(engine, query);
364
	                    status = true;
365
	                }
366
                } else if (resource.startsWith(RESOURCE_GENERATE_ID)) {
367
                	// generate an id
368
                    if (httpVerb == POST) {
369
                        generateIdentifier();
370
                        status = true;
371
                    }
372
                }
373
                
374
                if (!status) {
375
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
376
                }
377
            } else {
378
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
379
            }
380
        } catch (BaseException be) {
381
        	// report Exceptions as clearly as possible
382
        	OutputStream out = null;
383
			try {
384
				out = response.getOutputStream();
385
			} catch (IOException e) {
386
				logMetacat.error("Could not get output stream from response", e);
387
			}
388
            serializeException(be, out);
389
        } catch (Exception e) {
390
            // report Exceptions as clearly and generically as possible
391
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
392
        	OutputStream out = null;
393
			try {
394
				out = response.getOutputStream();
395
			} catch (IOException ioe) {
396
				logMetacat.error("Could not get output stream from response", ioe);
397
			}
398
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
399
            serializeException(se, out);
400
        }
401
    }
402
    
403

    
404
    private void doQuery(String engine, String query) {
405
    	
406
		OutputStream out = null;
407

    
408
    	try {
409
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter			
410
	    	if (engine == null) {
411
	    		// just looking for list of engines
412
	    		MNodeService mnode = MNodeService.getInstance(request);
413
    			mnode.setSession(session);
414
	    		QueryEngineList qel = mnode.listQueryEngines();
415
	    		response.setContentType("text/xml");
416
	            response.setStatus(200);
417
	            out = response.getOutputStream();
418
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
419
	            return;
420
	    	} else {
421
	    		if (query != null) {
422
	    			MNodeService mnode = MNodeService.getInstance(request);
423
	    			mnode.setSession(session);
424
	    			InputStream stream = mnode.query(engine, query);
425

    
426
	    			// set the content-type if we have it from the implementation
427
	    			if (stream instanceof ContentTypeInputStream) {
428
		    			//response.setContentType("application/octet-stream");
429
		    			//response.setContentType("text/xml");
430
	    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
431
	    			}
432
	                response.setStatus(200);
433
	                out = response.getOutputStream();
434
	                // write the results to the output stream
435
	                IOUtils.copyLarge(stream, out);
436
	                return;
437
	    		} else {
438
	    			MNodeService mnode = MNodeService.getInstance(request);
439
	    			mnode.setSession(session);
440
	    			QueryEngineDescription qed = mnode.getQueryEngineDescription(engine);
441
	    			response.setContentType("text/xml");
442
		            response.setStatus(200);
443
		            out = response.getOutputStream();
444
		            TypeMarshaller.marshalTypeToOutputStream(qed, out);
445
		            return;
446
	    		}
447
	    	}
448
	        
449
	        
450
    	} catch (BaseException be) {
451
        	// report Exceptions as clearly as possible
452
			try {
453
				out = response.getOutputStream();
454
			} catch (IOException e) {
455
				logMetacat.error("Could not get output stream from response", e);
456
			}
457
            serializeException(be, out);
458
        } catch (Exception e) {
459
            // report Exceptions as clearly and generically as possible
460
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
461
			try {
462
				out = response.getOutputStream();
463
			} catch (IOException ioe) {
464
				logMetacat.error("Could not get output stream from response", ioe);
465
			}
466
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
467
            serializeException(se, out);
468
        }
469
    }
470
    
471
    
472
    /**
473
     * Handles notification of system metadata changes for the given identifier
474
     * 
475
     * @param id  the identifier for the object
476
     * @throws InvalidToken 
477
     * @throws InvalidRequest 
478
     * @throws NotAuthorized 
479
     * @throws ServiceFailure 
480
     * @throws NotImplemented 
481
     */
482
    private void systemMetadataChanged() 
483
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
484
        InvalidToken {
485

    
486
        long serialVersion = 0L;
487
        String serialVersionStr = null;
488
        Date dateSysMetaLastModified = null;
489
        String dateSysMetaLastModifiedStr = null;
490
        Identifier pid = null;
491
        
492
        // mkae sure we have the multipart params
493
        try {
494
			initMultipartParams();
495
		} catch (Exception e1) {
496
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
497
		}
498
        
499
        // get the pid
500
        try {
501
        	String id = multipartparams.get("pid").get(0);
502
        	pid = new Identifier();
503
            pid.setValue(id);            
504
        } catch (NullPointerException e) {
505
            String msg = "The 'pid' must be provided as a parameter and was not.";
506
            logMetacat.error(msg);
507
            throw new InvalidRequest("1334", msg);
508
        }      
509
        
510
        // get the serialVersion
511
        try {
512
            serialVersionStr = multipartparams.get("serialVersion").get(0);
513
            serialVersion = new Long(serialVersionStr).longValue();
514
            
515
        } catch (NullPointerException e) {
516
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
517
            logMetacat.error(msg);
518
            throw new InvalidRequest("1334", msg);
519
            
520
        }       
521
        
522
        // get the dateSysMetaLastModified
523
        try {
524
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
525
            dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
526
            
527
        } catch (NullPointerException e) {
528
            String msg = 
529
                "The 'dateSysMetaLastModified' must be provided as a " + 
530
                "parameter and was not, or was an invalid representation of the timestamp.";
531
            logMetacat.error(msg);
532
            throw new InvalidRequest("1334", msg);
533
            
534
        }       
535
        
536
        // call the service
537
        MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
538
        response.setStatus(200);
539
    }
540
    
541
    /**
542
     * Handles identifier generation calls
543
     * 
544
     * @throws InvalidRequest 
545
     * @throws NotImplemented 
546
     * @throws NotAuthorized 
547
     * @throws ServiceFailure 
548
     * @throws InvalidToken 
549
     * @throws IOException 
550
     * @throws JiBXException 
551
     */
552
    private void generateIdentifier() throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, IOException, JiBXException {
553
        
554
        // make sure we have the multipart params
555
        try {
556
			initMultipartParams();
557
		} catch (Exception e1) {
558
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
559
		}
560
        
561
        // get the scheme
562
		String scheme = null;
563
        try {
564
        	scheme = multipartparams.get("scheme").get(0);	           
565
        } catch (NullPointerException e) {
566
            String msg = "The 'scheme' parameter was not provided, using default";
567
            logMetacat.warn(msg);
568
        }
569
        
570
        // get the fragment
571
		String fragment = null;
572
        try {
573
        	fragment = multipartparams.get("fragment").get(0);	           
574
        } catch (NullPointerException e) {
575
            String msg = "The 'fragment' parameter was not provided, using default";
576
            logMetacat.warn(msg);
577
        }
578

    
579
        // call the service
580
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
581
        response.setStatus(200);
582
        response.setContentType("text/xml");
583
        OutputStream out = response.getOutputStream();
584
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
585
    }
586

    
587
    /**
588
     * Checks the access policy
589
     * @param id
590
     * @return
591
     * @throws ServiceFailure
592
     * @throws InvalidToken
593
     * @throws NotFound
594
     * @throws NotAuthorized
595
     * @throws NotImplemented
596
     * @throws InvalidRequest
597
     */
598
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
599
		Identifier pid = new Identifier();
600
		pid.setValue(id);
601
		Permission permission = null;
602
		try {
603
			String perm = params.get("action")[0];
604
			permission = Permission.convert(perm);
605
		} catch (Exception e) {
606
			logMetacat.warn("No permission specified");
607
		}
608
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
609
		response.setStatus(200);
610
		response.setContentType("text/xml");
611
		return result;
612
    }
613
    
614
    /**
615
     * Processes failed synchronization message
616
     * @throws NotImplemented
617
     * @throws ServiceFailure
618
     * @throws NotAuthorized
619
     * @throws InvalidRequest
620
     * @throws JiBXException
621
     * @throws IllegalAccessException 
622
     * @throws InstantiationException 
623
     * @throws IOException 
624
     */
625
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
626
    	SynchronizationFailed syncFailed = null;
627
		try {
628
			syncFailed = collectSynchronizationFailed();
629
		} catch (ParserConfigurationException e) {
630
			throw new ServiceFailure("2161", e.getMessage());
631
		} catch (SAXException e) {
632
			throw new ServiceFailure("2161", e.getMessage());
633
		}
634
		
635
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
636
    }
637
    
638

    
639
    /**
640
     * Handles the monitoring resources
641
     * @return
642
     * @throws NotFound
643
     * @throws ParseException
644
     * @throws NotImplemented
645
     * @throws ServiceFailure
646
     * @throws NotAuthorized
647
     * @throws InvalidRequest
648
     * @throws InsufficientResources
649
     * @throws UnsupportedType
650
     * @throws IOException
651
     * @throws JiBXException
652
     */
653
    private boolean monitor(String pathInfo) 
654
      throws NotFound, ParseException, NotImplemented, ServiceFailure, 
655
      NotAuthorized, InvalidRequest, InsufficientResources, UnsupportedType, 
656
      IOException, JiBXException {
657
    	logMetacat.debug("processing monitor request");
658
        
659
        logMetacat.debug("verb is GET");
660
        logMetacat.debug("pathInfo is " + pathInfo);
661
        
662
        if (pathInfo.toLowerCase().equals("status")) {
663
            logMetacat.debug("processing status request");
664
            // TODO: implement in MNCore
665
            //MNodeService.getInstance().getStatus();
666
            return false;
667
            
668
        } else if (pathInfo.toLowerCase().equals("object")) {
669
            logMetacat.debug("processing object request");
670
            Identifier pid = null;
671
            ObjectFormat format = null;
672
            if (params.containsKey("format")) {
673
                String f = params.get("format")[0];
674
                format = ObjectFormatCache.getInstance().getFormat(f);
675
            }
676
            if (params.containsKey("pid")) {
677
                String id = params.get("pid")[0];
678
                pid = new Identifier();
679
                pid.setValue(id);
680
            }
681
            
682
            // TODO: implement in MNCore
683
            //ObjectStatistics objectStats = MNodeService.getInstance().getObjectStatistics(format, pid);
684
            return false;
685
            
686
        } else if (pathInfo.toLowerCase().equals("event")) {
687
            logMetacat.debug("processing event request");
688
            ObjectFormatIdentifier fmtid = null;
689
            String fromDateStr = null;
690
            Date fromDate = null;
691
            String toDateStr = null;
692
            Date toDate = null;
693
            String requestor = null;
694
            Subject subject = null;
695
            String eventName = null;
696
            Event event = null;
697

    
698
            if (params.containsKey("formatId")) {
699
                String f = params.get("formatId")[0];
700
                fmtid = ObjectFormatCache.getInstance().getFormat(f).getFormatId();
701
            }
702
            
703
            if (params.containsKey("fromDate")) {
704
                fromDateStr = params.get("fromDate")[0];
705
                fromDate = getDateAsUTC(fromDateStr);
706
            }
707
            
708
            if (params.containsKey("toDate")) {
709
              toDateStr = params.get("toDate")[0];
710
              toDate = getDateAsUTC(toDateStr);
711
            }
712
            
713
            if (params.containsKey("requestor")) {
714
            	requestor = params.get("requestor")[0];
715
            	subject = new Subject();
716
            	subject.setValue(requestor);
717
            }
718
            
719
            if (params.containsKey("event")) {
720
            	eventName = params.get("event")[0];
721
                event = Event.convert(eventName);
722
            }
723
            
724
            MonitorList monitorList = MNodeService.getInstance(request).getOperationStatistics(session, fromDate, toDate, subject, event, fmtid);
725
            
726
            OutputStream out = response.getOutputStream();
727
            response.setStatus(200);
728
            response.setContentType("text/xml");
729
            
730
            TypeMarshaller.marshalTypeToOutputStream(monitorList, out);
731
            
732
            return true;
733
            
734
        }
735
        
736
        return false;
737
    }
738
    
739
    /*
740
     * Parse an ISO8601 date string, returning a Date in UTC time if the string
741
     * ends in 'Z'.
742
     * 
743
     * @param fromDateStr -  the date string to be parsed
744
     * @return date -  the date object represented by the string
745
     */
746
    private Date getDateAsUTC(String fromDateStr)
747
      throws ParseException {
748

    
749
    	Date date = null;
750
    	
751
    	try {
752
    		// try the expected date format
753
        // a date format for date string arguments
754
        DateFormat dateFormat = 
755
        	new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
756

    
757
	      date = dateFormat.parse(fromDateStr);
758
      
759
    	} catch (ParseException e) {
760
    		// try the date with the UTC indicator
761
        DateFormat utcDateFormat = 
762
        	new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
763
        utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
764
        date = utcDateFormat.parse(fromDateStr);
765
        
766
      }
767
    	
768
    	return date;
769
    }
770

    
771
		/**
772
     * Calculate the checksum 
773
     * @throws NotImplemented
774
     * @throws JiBXException
775
     * @throws IOException
776
     * @throws InvalidToken
777
     * @throws ServiceFailure
778
     * @throws NotAuthorized
779
     * @throws NotFound
780
     * @throws InvalidRequest
781
     */
782
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
783
    	String checksumAlgorithm = "MD5";
784
    	try {
785
    		checksumAlgorithm = PropertyService.getProperty("dataone.checksumAlgorithm.default");
786
        } catch(Exception e) {
787
        	logMetacat.warn("Could not lookup configured default checksum algorithm, using: " + checksumAlgorithm);
788
        }    
789
        
790
        Identifier pidid = new Identifier();
791
        pidid.setValue(pid);
792
        try {
793
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
794
        } catch(Exception e) {
795
            //do nothing.  use the default
796
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
797
        }
798
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
799
        
800
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
801
        logMetacat.debug("got checksum " + c.getValue());
802
        response.setStatus(200);
803
        logMetacat.debug("serializing response");
804
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
805
        logMetacat.debug("done serializing response.");
806
        
807
    }
808
    
809
	/**
810
     * handle the replicate action for MN
811
	 * @throws JiBXException 
812
	 * @throws FileUploadException 
813
	 * @throws IOException 
814
	 * @throws InvalidRequest 
815
	 * @throws ServiceFailure 
816
	 * @throws UnsupportedType 
817
	 * @throws InsufficientResources 
818
	 * @throws NotAuthorized 
819
	 * @throws NotImplemented 
820
	 * @throws IllegalAccessException 
821
	 * @throws InstantiationException 
822
	 * @throws InvalidToken 
823
     */
824
    private void replicate() 
825
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException, 
826
        JiBXException, NotImplemented, NotAuthorized, InsufficientResources, 
827
        UnsupportedType, InstantiationException, IllegalAccessException, InvalidToken {
828

    
829
        logMetacat.debug("in POST replicate()");
830
        
831
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
832
        boolean allowed = false;
833
        if (session == null) {
834
        	String msg = "No session was provided.";
835
            NotAuthorized failure = new NotAuthorized("2152", msg);
836
        	throw failure;
837
        } else {
838
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
839
        	if (!allowed) {
840
        		String msg = "User is not an admin user";
841
                NotAuthorized failure = new NotAuthorized("2152", msg);
842
            	throw failure;
843
        	}
844
        }
845
        
846
        //parse the systemMetadata
847
        final SystemMetadata sysmeta = collectSystemMetadata();
848
        
849
        String sn = multipartparams.get("sourceNode").get(0);
850
        logMetacat.debug("sourceNode: " + sn);
851
        final NodeReference sourceNode = new NodeReference();
852
        sourceNode.setValue(sn);
853
        
854
        // run it in a thread to avoid connection timeout
855
        Runnable runner = new Runnable() {
856
			@Override
857
			public void run() {
858
				try {
859
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
860
				} catch (Exception e) {
861
					logMetacat.error("Error running replication: " + e.getMessage(), e);
862
					throw new RuntimeException(e.getMessage(), e);
863
				}
864
			}
865
    	};
866
    	// submit the task, and that's it
867
    	executor.submit(runner);
868
        
869
    	// thread was started, so we return success
870
        response.setStatus(200);
871
        
872
    }
873

    
874
    /**
875
     * Handle the getReplica action for the MN
876
     * @param id  the identifier for the object
877
     * @throws NotFound 
878
     * @throws ServiceFailure 
879
     * @throws NotImplemented 
880
     * @throws NotAuthorized 
881
     * @throws InvalidToken 
882
     * @throws InvalidRequest 
883
     */
884
    private void getReplica(String id) 
885
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
886
        ServiceFailure, NotFound {
887
        
888
        Identifier pid = new Identifier();
889
        pid.setValue(id);
890
        OutputStream out = null;
891
        InputStream dataBytes = null;
892
                
893
        try {
894
            // call the service
895
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
896

    
897
            response.setContentType("application/octet-stream");
898
            response.setStatus(200);
899
            out = response.getOutputStream();
900
            // write the object to the output stream
901
            IOUtils.copyLarge(dataBytes, out);
902
            
903
        } catch (IOException e) {
904
            String msg = "There was an error writing the output: " + e.getMessage();
905
            logMetacat.error(msg);
906
            throw new ServiceFailure("2181", msg);
907
        
908
        }
909

    
910
    }
911

    
912
    /**
913
     * Get the Node information
914
     * 
915
     * @throws JiBXException
916
     * @throws IOException
917
     * @throws InvalidRequest 
918
     * @throws ServiceFailure 
919
     * @throws NotAuthorized 
920
     * @throws NotImplemented 
921
     */
922
    private void node() 
923
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
924
        
925
        Node n = MNodeService.getInstance(request).getCapabilities();
926
        
927
        response.setContentType("text/xml");
928
        response.setStatus(200);
929
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
930
        
931
    }
932
    
933
    /**
934
     * MN_crud.describe()
935
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
936
     * @param pid
937
     * @throws InvalidRequest 
938
     * @throws NotImplemented 
939
     * @throws NotFound 
940
     * @throws NotAuthorized 
941
     * @throws ServiceFailure 
942
     * @throws InvalidToken 
943
     */
944
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
945
    {
946
        
947
        response.setContentType("text/xml");
948

    
949
        Identifier id = new Identifier();
950
        id.setValue(pid);
951

    
952
        DescribeResponse dr = null;
953
        try {
954
        	dr = MNodeService.getInstance(request).describe(session, id);
955
        } catch (BaseException e) {
956
        	response.setStatus(e.getCode());
957
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
958
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
959
            response.addHeader("DataONE-Exception-Description", e.getDescription());
960
            response.addHeader("DataONE-Exception-PID", id.getValue());
961
            return;
962
		}
963
        
964
        response.setStatus(200);
965
        
966
        //response.addHeader("pid", pid);
967
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
968
        response.addHeader("Content-Length", dr.getContent_Length() + "");
969
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
970
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
971
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
972

    
973
        
974
    }
975
    
976
    /**
977
     * get the logs based on passed params.  Available 
978
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
979
     * for more info
980
     * @throws NotImplemented 
981
     * @throws InvalidRequest 
982
     * @throws NotAuthorized 
983
     * @throws ServiceFailure 
984
     * @throws InvalidToken 
985
     * @throws IOException 
986
     * @throws JiBXException 
987
     */
988
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
989
    {
990
            
991
        Date fromDate = null;
992
        Date toDate = null;
993
        Event event = null;
994
        Integer start = null;
995
        Integer count = null;
996
        String pidFilter = null;
997
        
998
        try {
999
        	String fromDateS = params.get("fromDate")[0];
1000
            logMetacat.debug("param fromDateS: " + fromDateS);
1001
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1002
        } catch (Exception e) {
1003
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1004
        }
1005
        try {
1006
        	String toDateS = params.get("toDate")[0];
1007
            logMetacat.debug("param toDateS: " + toDateS);
1008
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1009
        } catch (Exception e) {
1010
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1011
		}
1012
        try {
1013
        	String eventS = params.get("event")[0];
1014
            event = Event.convert(eventS);
1015
        } catch (Exception e) {
1016
        	logMetacat.warn("Could not parse event: " + e.getMessage());
1017
		}
1018
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
1019
        
1020
        try {
1021
        	start =  Integer.parseInt(params.get("start")[0]);
1022
        } catch (Exception e) {
1023
			logMetacat.warn("Could not parse start: " + e.getMessage());
1024
		}
1025
        try {
1026
        	count =  Integer.parseInt(params.get("count")[0]);
1027
        } catch (Exception e) {
1028
			logMetacat.warn("Could not parse count: " + e.getMessage());
1029
		}
1030
        
1031
        try {
1032
            pidFilter = params.get("pidFilter")[0];
1033
        } catch (Exception e) {
1034
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1035
        }
1036
        
1037
        logMetacat.debug("calling getLogRecords");
1038
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1039
        
1040
        OutputStream out = response.getOutputStream();
1041
        response.setStatus(200);
1042
        response.setContentType("text/xml");
1043
        
1044
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1045
        
1046
    }
1047
    
1048
    
1049
    
1050
    /**
1051
     * Implements REST version of DataONE CRUD API --> get
1052
     * @param pid ID of data object to be read
1053
     * @throws NotImplemented 
1054
     * @throws InvalidRequest 
1055
     * @throws NotFound 
1056
     * @throws NotAuthorized 
1057
     * @throws ServiceFailure 
1058
     * @throws InvalidToken 
1059
     * @throws IOException 
1060
     * @throws JiBXException 
1061
     */
1062
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1063
        OutputStream out = null;
1064
        
1065
        if (pid != null) { //get a specific document                
1066
            Identifier id = new Identifier();
1067
            id.setValue(pid);
1068
                
1069
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1070
            
1071
            // set the headers for the content
1072
            String mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1073
            if (mimeType == null) {
1074
            	mimeType = "application/octet-stream";
1075
            }
1076
            String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1077
            String filename = id.getValue();
1078
            if (extension != null) {
1079
            	filename = id.getValue() + extension;
1080
            }
1081
            response.setContentType(mimeType);
1082
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1083
            
1084
            InputStream data = MNodeService.getInstance(request).get(session, id);
1085

    
1086
            out = response.getOutputStream();  
1087
            IOUtils.copyLarge(data, out);
1088
            
1089
        }
1090
        else
1091
        { //call listObjects with specified params
1092
            Date startTime = null;
1093
            Date endTime = null;
1094
            ObjectFormatIdentifier formatId = null;
1095
            boolean replicaStatus = true;
1096
            int start = 0;
1097
            //TODO: make the max count into a const
1098
            int count = 1000;
1099
            Enumeration paramlist = request.getParameterNames();
1100
            while (paramlist.hasMoreElements()) 
1101
            { //parse the params and make the crud call
1102
                String name = (String) paramlist.nextElement();
1103
                String[] value = (String[])request.getParameterValues(name);
1104

    
1105
                if (name.equals("fromDate") && value != null)
1106
                {
1107
                    try
1108
                    {
1109
                      //startTime = dateFormat.parse(value[0]);
1110
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1111
                        //startTime = parseDateAndConvertToGMT(value[0]);
1112
                    }
1113
                    catch(Exception e)
1114
                    {  //if we can't parse it, just don't use the fromDate param
1115
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1116
                        startTime = null;
1117
                    }
1118
                }
1119
                else if(name.equals("toDate") && value != null)
1120
                {
1121
                    try
1122
                    {
1123
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1124
                    }
1125
                    catch(Exception e)
1126
                    {  //if we can't parse it, just don't use the toDate param
1127
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1128
                        endTime = null;
1129
                    }
1130
                }
1131
                else if(name.equals("formatId") && value != null) 
1132
                {
1133
                	formatId = new ObjectFormatIdentifier();
1134
                	formatId.setValue(value[0]);
1135
                }
1136
                else if(name.equals("replicaStatus") && value != null)
1137
                {
1138
                    if(value != null && 
1139
                       value.length > 0 && 
1140
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1141
                    {
1142
                        replicaStatus = false;
1143
                    }
1144
                }
1145
                else if(name.equals("start") && value != null)
1146
                {
1147
                    start = new Integer(value[0]).intValue();
1148
                }
1149
                else if(name.equals("count") && value != null)
1150
                {
1151
                    count = new Integer(value[0]).intValue();
1152
                }
1153
            }
1154
            //make the crud call
1155
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1156
                    " endTime: " + endTime + " formatId: " + 
1157
                    formatId + " replicaStatus: " + replicaStatus + 
1158
                    " start: " + start + " count: " + count);
1159
           
1160
            ObjectList ol = 
1161
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1162
           			formatId, replicaStatus, start, count);
1163
           
1164
            out = response.getOutputStream();  
1165
            response.setStatus(200);
1166
            response.setContentType("text/xml");
1167
            // Serialize and write it to the output stream
1168
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1169
            
1170
        }
1171
        
1172
    }
1173
    
1174

    
1175
    /**
1176
     * Retrieve System Metadata
1177
     * @param pid
1178
     * @throws InvalidToken
1179
     * @throws ServiceFailure
1180
     * @throws NotAuthorized
1181
     * @throws NotFound
1182
     * @throws InvalidRequest
1183
     * @throws NotImplemented
1184
     * @throws IOException
1185
     * @throws JiBXException
1186
     */
1187
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1188

    
1189
        Identifier id = new Identifier();
1190
        id.setValue(pid);
1191
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1192
        
1193
        response.setContentType("text/xml");
1194
        response.setStatus(200);
1195
        OutputStream out = response.getOutputStream();
1196
        
1197
        // Serialize and write it to the output stream
1198
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1199
   }
1200
    
1201
    
1202
    /**
1203
     * Inserts or updates the object
1204
     * 
1205
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1206
     *               is the existing pid.  If insert, the pid is the new one
1207
     * @throws InvalidRequest 
1208
     * @throws ServiceFailure 
1209
     * @throws JiBXException 
1210
     * @throws NotImplemented 
1211
     * @throws InvalidSystemMetadata 
1212
     * @throws InsufficientResources 
1213
     * @throws UnsupportedType 
1214
     * @throws IdentifierNotUnique 
1215
     * @throws NotAuthorized 
1216
     * @throws InvalidToken 
1217
     * @throws NotFound 
1218
     * @throws IOException 
1219
     * @throws IllegalAccessException 
1220
     * @throws InstantiationException 
1221
     */
1222
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1223
       
1224
    	// Read the incoming data from its Mime Multipart encoding
1225
    	Map<String, File> files = collectMultipartFiles();
1226
               
1227
    	Identifier pid = new Identifier();
1228
        if (trailingPid == null) {
1229
	        // get the pid string from the body and set the value
1230
	        String pidString = multipartparams.get("pid").get(0);
1231
	        if (pidString != null) {
1232
            pid.setValue(pidString);
1233
            
1234
          } else {
1235
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1236
              
1237
          }
1238
        } else {
1239
        	// use the pid included in the URL
1240
        	pid.setValue(trailingPid);
1241
        }
1242
        logMetacat.debug("putObject with pid " + pid.getValue());
1243
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1244

    
1245
        InputStream object = null;
1246
        InputStream sysmeta = null;
1247
        File smFile = files.get("sysmeta");
1248
        sysmeta = new FileInputStream(smFile);
1249
        File objFile = files.get("object");
1250
        object = new FileInputStream(objFile);
1251
        
1252
        // ensure we have the object bytes
1253
        if  ( objFile == null ) {
1254
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1255
            
1256
        }
1257
        
1258
        // ensure we have the system metadata
1259
        if  ( smFile == null ) {
1260
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1261
            
1262
        }
1263
        
1264
        response.setStatus(200);
1265
        response.setContentType("text/xml");
1266
        OutputStream out = response.getOutputStream();
1267
        
1268
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1269
            // handle inserts
1270
            logMetacat.debug("Commence creation...");
1271
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1272

    
1273
            logMetacat.debug("creating object with pid " + pid.getValue());
1274
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1275
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1276
            
1277
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1278
        	// handle updates
1279
        	
1280
            // construct pids
1281
            Identifier newPid = null;
1282
            try {
1283
            	String newPidString = multipartparams.get("newPid").get(0);
1284
            	newPid = new Identifier();
1285
            	newPid.setValue(newPidString);
1286
            } catch (Exception e) {
1287
				logMetacat.error("Could not get newPid from request");
1288
			}
1289
            logMetacat.debug("Commence update...");
1290
            
1291
            // get the systemmetadata object
1292
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1293

    
1294
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1295
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1296
        } else {
1297
            throw new InvalidRequest("1000", "Operation must be create or update.");
1298
        }
1299
   
1300
    }
1301

    
1302
    /**
1303
     * Handle delete 
1304
     * @param pid ID of data object to be deleted
1305
     * @throws IOException
1306
     * @throws InvalidRequest 
1307
     * @throws NotImplemented 
1308
     * @throws NotFound 
1309
     * @throws NotAuthorized 
1310
     * @throws ServiceFailure 
1311
     * @throws InvalidToken 
1312
     * @throws JiBXException 
1313
     */
1314
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1315
    {
1316

    
1317
        OutputStream out = response.getOutputStream();
1318
        response.setStatus(200);
1319
        response.setContentType("text/xml");
1320

    
1321
        Identifier id = new Identifier();
1322
        id.setValue(pid);
1323

    
1324
        logMetacat.debug("Calling delete");
1325
        MNodeService.getInstance(request).delete(session, id);
1326
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1327
        
1328
    }
1329
    
1330
    /**
1331
     * Archives the given pid
1332
     * @param pid
1333
     * @throws InvalidToken
1334
     * @throws ServiceFailure
1335
     * @throws NotAuthorized
1336
     * @throws NotFound
1337
     * @throws NotImplemented
1338
     * @throws IOException
1339
     * @throws JiBXException
1340
     */
1341
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1342

    
1343
        OutputStream out = response.getOutputStream();
1344
        response.setStatus(200);
1345
        response.setContentType("text/xml");
1346

    
1347
        Identifier id = new Identifier();
1348
        id.setValue(pid);
1349

    
1350
        logMetacat.debug("Calling archive");
1351
        MNodeService.getInstance(request).archive(session, id);
1352
        
1353
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1354
        
1355
    }
1356

    
1357
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1358
		
1359
		// Read the incoming data from its Mime Multipart encoding
1360
		logMetacat.debug("Disassembling MIME multipart form");
1361
		InputStream sf = null;
1362
	
1363
		// handle MMP inputs
1364
		File tmpDir = getTempDirectory();
1365
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1366
		MultipartRequestResolver mrr = 
1367
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1368
		MultipartRequest mr = null;
1369
		try {
1370
			mr = mrr.resolveMultipart(request);
1371
		} catch (Exception e) {
1372
			throw new ServiceFailure("2161", 
1373
					"Could not resolve multipart: " + e.getMessage());
1374
		}
1375
		logMetacat.debug("resolved multipart request");
1376
		Map<String, File> files = mr.getMultipartFiles();
1377
		if (files == null || files.keySet() == null) {
1378
			throw new InvalidRequest("2163",
1379
					"must have multipart file with name 'message'");
1380
		}
1381
		logMetacat.debug("got multipart files");
1382
	
1383
		multipartparams = mr.getMultipartParameters();
1384
	
1385
		File sfFile = files.get("message");
1386
		if (sfFile == null) {
1387
			throw new InvalidRequest("2163",
1388
					"Missing the required file-part 'message' from the multipart request.");
1389
		}
1390
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1391
		sf = new FileInputStream(sfFile);
1392
	
1393
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1394
		return syncFailed;
1395
	}
1396

    
1397
}
(8-8/9)