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: 2014-07-23 16:19:48 -0700 (Wed, 23 Jul 2014) $'
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.v1;
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.util.Date;
31
import java.util.Enumeration;
32
import java.util.Map;
33
import java.util.concurrent.ExecutorService;
34
import java.util.concurrent.Executors;
35

    
36
import javax.servlet.ServletContext;
37
import javax.servlet.http.HttpServletRequest;
38
import javax.servlet.http.HttpServletResponse;
39
import javax.xml.parsers.ParserConfigurationException;
40

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

    
79
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeInputStream;
80
import edu.ucsb.nceas.metacat.dataone.v1.MNodeService;
81
import edu.ucsb.nceas.metacat.properties.PropertyService;
82
import edu.ucsb.nceas.metacat.restservice.D1ResourceHandler;
83
import edu.ucsb.nceas.metacat.util.DeleteOnCloseFileInputStream;
84
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
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
 * 	MNStorage - DONE
111
 * 		create() - POST /d1/mn/object/PID
112
 * 		update() - PUT /d1/mn/object/PID
113
 * 		delete() - DELETE /d1/mn/object/PID
114
 * 		archive() - PUT /d1/mn/archive/PID
115

    
116
 *    systemMetadataChanged() - POST /dirtySystemMetadata/PID
117
 * 	
118
 * 	MNReplication
119
 * 		replicate() - POST /d1/mn/replicate
120
 *    getReplica() - GET /d1/mn/replica
121
 * 
122
 * ******************
123
 * @author leinfelder
124
 *
125
 */
126
public class MNResourceHandler extends D1ResourceHandler {
127

    
128
    // MN-specific API Resources
129
    protected static final String RESOURCE_MONITOR = "monitor";
130
    protected static final String RESOURCE_REPLICATE = "replicate";
131
    protected static final String RESOURCE_REPLICAS = "replica";
132
    protected static final String RESOURCE_NODE = "node";
133
    protected static final String RESOURCE_ERROR = "error";
134
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
135
    protected static final String RESOURCE_GENERATE_ID = "generate";
136
    protected static final String RESOURCE_PUBLISH = "publish";
137
    protected static final String RESOURCE_PACKAGE = "package";
138
    protected static final String RESOURCE_VIEWS = "views";
139

    
140

    
141
    
142
    // shared executor
143
	private static ExecutorService executor = null;
144

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

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

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

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

    
332
	                String engine = null;
333
	                String query = null;
334

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

    
359
	                }
360
	                logMetacat.debug("verb:" + httpVerb);
361
	                if (httpVerb == GET) {
362
	                    doQuery(engine, query);
363
	                    status = true;
364
	                }
365
                } else if (resource.startsWith(RESOURCE_GENERATE_ID)) {
366
                	// generate an id
367
                    if (httpVerb == POST) {
368
                        generateIdentifier();
369
                        status = true;
370
                    }
371
                } else if (resource.startsWith(RESOURCE_PUBLISH)) {
372
                    logMetacat.debug("Using resource: " + RESOURCE_PUBLISH);
373
                    // PUT
374
                    if (httpVerb == PUT) {
375
                    	// after the command
376
                        extra = parseTrailing(resource, RESOURCE_PUBLISH);
377
                        publish(extra);
378
                        status = true;
379
                    }  
380
                } else if (resource.startsWith(RESOURCE_PACKAGE)) {
381
                    logMetacat.debug("Using resource: " + RESOURCE_PACKAGE);
382
                    // get
383
                    if (httpVerb == GET) {
384
                    	// after the command
385
                        extra = parseTrailing(resource, RESOURCE_PACKAGE);
386
                        getPackage(extra);
387
                        status = true;
388
                    }  
389
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
390
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
391
	                // after the command
392
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
393
	                logMetacat.debug("view extra: " + extra);
394

    
395
	                String format = null;
396
	                String pid = null;
397

    
398
	                if (extra != null) {
399
		                // get the format
400
		                int formatIndex = extra.length();
401
		                if (extra.indexOf("/") > -1) {
402
		                	formatIndex = extra.indexOf("/");
403
		                }
404
		                format = extra.substring(0, formatIndex);
405
		                logMetacat.debug("view format: " + format);
406
		                
407
		                // get the pid if it is there
408
		                pid = extra.substring(formatIndex, extra.length());
409
		                if (pid != null && pid.length() == 0) {
410
		                	pid = null;
411
		                } else {
412
		                	if (pid.startsWith("/")) {
413
		                		pid = pid.substring(1);
414
		                    }
415
		                }
416
		                logMetacat.debug("pid: " + pid);
417

    
418
	                }
419
	                logMetacat.debug("verb:" + httpVerb);
420
	                if (httpVerb == GET) {
421
	                    doViews(format, pid);
422
	                    status = true;
423
	                }
424
                } 
425
                
426
                if (!status) {
427
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
428
                }
429
            } else {
430
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
431
            }
432
        } catch (BaseException be) {
433
        	// report Exceptions as clearly as possible
434
        	OutputStream out = null;
435
			try {
436
				out = response.getOutputStream();
437
			} catch (IOException e) {
438
				logMetacat.error("Could not get output stream from response", e);
439
			}
440
            serializeException(be, out);
441
        } catch (Exception e) {
442
            // report Exceptions as clearly and generically as possible
443
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
444
        	OutputStream out = null;
445
			try {
446
				out = response.getOutputStream();
447
			} catch (IOException ioe) {
448
				logMetacat.error("Could not get output stream from response", ioe);
449
			}
450
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
451
            serializeException(se, out);
452
        }
453
    }
454
    
455

    
456
    private void doQuery(String engine, String query) {
457
    	
458
		OutputStream out = null;
459

    
460
    	try {
461
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter			
462
	    	if (engine == null) {
463
	    		// just looking for list of engines
464
	    		MNodeService mnode = MNodeService.getInstance(request);
465
    			mnode.setSession(session);
466
	    		QueryEngineList qel = mnode.listQueryEngines();
467
	    		response.setContentType("text/xml");
468
	            response.setStatus(200);
469
	            out = response.getOutputStream();
470
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
471
	            return;
472
	    	} else {
473
	    		if (query != null) {
474
	    			MNodeService mnode = MNodeService.getInstance(request);
475
	    			mnode.setSession(session);
476
	    			InputStream stream = mnode.query(engine, query);
477

    
478
	    			// set the content-type if we have it from the implementation
479
	    			if (stream instanceof ContentTypeInputStream) {
480
		    			//response.setContentType("application/octet-stream");
481
		    			//response.setContentType("text/xml");
482
	    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
483
	    			}
484
	                response.setStatus(200);
485
	                out = response.getOutputStream();
486
	                // write the results to the output stream
487
	                IOUtils.copyLarge(stream, out);
488
	                return;
489
	    		} else {
490
	    			MNodeService mnode = MNodeService.getInstance(request);
491
	    			mnode.setSession(session);
492
	    			QueryEngineDescription qed = mnode.getQueryEngineDescription(engine);
493
	    			response.setContentType("text/xml");
494
		            response.setStatus(200);
495
		            out = response.getOutputStream();
496
		            TypeMarshaller.marshalTypeToOutputStream(qed, out);
497
		            return;
498
	    		}
499
	    	}
500
	        
501
	        
502
    	} catch (BaseException be) {
503
        	// report Exceptions as clearly as possible
504
			try {
505
				out = response.getOutputStream();
506
			} catch (IOException e) {
507
				logMetacat.error("Could not get output stream from response", e);
508
			}
509
            serializeException(be, out);
510
        } catch (Exception e) {
511
            // report Exceptions as clearly and generically as possible
512
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
513
			try {
514
				out = response.getOutputStream();
515
			} catch (IOException ioe) {
516
				logMetacat.error("Could not get output stream from response", ioe);
517
			}
518
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
519
            serializeException(se, out);
520
        }
521
    }
522
    
523
    private void doViews(String format, String pid) {
524
    	
525
		OutputStream out = null;
526
		MNodeService mnode = MNodeService.getInstance(request);
527

    
528
    	try {
529
    		// get a list of views
530
    		if (pid != null) {
531
    			Identifier identifier = new Identifier();
532
    			identifier.setValue(pid);
533
				InputStream stream = mnode.view(session, format, identifier);
534

    
535
    			// set the content-type if we have it from the implementation
536
    			if (stream instanceof ContentTypeInputStream) {
537
    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
538
    			}
539
                response.setStatus(200);
540
                out = response.getOutputStream();
541
                // write the results to the output stream
542
                IOUtils.copyLarge(stream, out);
543
                return;
544
    		} else {
545
    			// TODO: list the registered views
546
                BaseException ni = new NotImplemented("9999", "MN.listViews() is not implemented at this node");
547
				throw ni;
548
    		}
549
	    	
550
	        
551
    	} catch (BaseException be) {
552
        	// report Exceptions as clearly as possible
553
			try {
554
				out = response.getOutputStream();
555
			} catch (IOException e) {
556
				logMetacat.error("Could not get output stream from response", e);
557
			}
558
            serializeException(be, out);
559
        } catch (Exception e) {
560
            // report Exceptions as clearly and generically as possible
561
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
562
			try {
563
				out = response.getOutputStream();
564
			} catch (IOException ioe) {
565
				logMetacat.error("Could not get output stream from response", ioe);
566
			}
567
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
568
            serializeException(se, out);
569
        }
570
    }
571
    
572
    /**
573
     * Handles notification of system metadata changes for the given identifier
574
     * 
575
     * @param id  the identifier for the object
576
     * @throws InvalidToken 
577
     * @throws InvalidRequest 
578
     * @throws NotAuthorized 
579
     * @throws ServiceFailure 
580
     * @throws NotImplemented 
581
     */
582
    private void systemMetadataChanged() 
583
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
584
        InvalidToken {
585

    
586
        long serialVersion = 0L;
587
        String serialVersionStr = null;
588
        Date dateSysMetaLastModified = null;
589
        String dateSysMetaLastModifiedStr = null;
590
        Identifier pid = null;
591
        
592
        // mkae sure we have the multipart params
593
        try {
594
			initMultipartParams();
595
		} catch (Exception e1) {
596
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
597
		}
598
        
599
        // get the pid
600
        try {
601
        	String id = multipartparams.get("pid").get(0);
602
        	pid = new Identifier();
603
            pid.setValue(id);            
604
        } catch (NullPointerException e) {
605
            String msg = "The 'pid' must be provided as a parameter and was not.";
606
            logMetacat.error(msg);
607
            throw new InvalidRequest("1334", msg);
608
        }      
609
        
610
        // get the serialVersion
611
        try {
612
            serialVersionStr = multipartparams.get("serialVersion").get(0);
613
            serialVersion = new Long(serialVersionStr).longValue();
614
            
615
        } catch (NullPointerException e) {
616
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
617
            logMetacat.error(msg);
618
            throw new InvalidRequest("1334", msg);
619
            
620
        }       
621
        
622
        // get the dateSysMetaLastModified
623
        try {
624
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
625
            dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
626
            
627
        } catch (NullPointerException e) {
628
            String msg = 
629
                "The 'dateSysMetaLastModified' must be provided as a " + 
630
                "parameter and was not, or was an invalid representation of the timestamp.";
631
            logMetacat.error(msg);
632
            throw new InvalidRequest("1334", msg);
633
            
634
        }       
635
        
636
        // call the service
637
        MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
638
        response.setStatus(200);
639
    }
640
    
641
    /**
642
     * Handles identifier generation calls
643
     * 
644
     * @throws InvalidRequest 
645
     * @throws NotImplemented 
646
     * @throws NotAuthorized 
647
     * @throws ServiceFailure 
648
     * @throws InvalidToken 
649
     * @throws IOException 
650
     * @throws JiBXException 
651
     */
652
    private void generateIdentifier() throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, IOException, JiBXException {
653
        
654
        // make sure we have the multipart params
655
        try {
656
			initMultipartParams();
657
		} catch (Exception e1) {
658
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
659
		}
660
        
661
        // get the scheme
662
		String scheme = null;
663
        try {
664
        	scheme = multipartparams.get("scheme").get(0);	           
665
        } catch (NullPointerException e) {
666
            String msg = "The 'scheme' parameter was not provided, using default";
667
            logMetacat.warn(msg);
668
        }
669
        
670
        // get the fragment
671
		String fragment = null;
672
        try {
673
        	fragment = multipartparams.get("fragment").get(0);	           
674
        } catch (NullPointerException e) {
675
            String msg = "The 'fragment' parameter was not provided, using default";
676
            logMetacat.warn(msg);
677
        }
678

    
679
        // call the service
680
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
681
        response.setStatus(200);
682
        response.setContentType("text/xml");
683
        OutputStream out = response.getOutputStream();
684
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
685
    }
686

    
687
    /**
688
     * Checks the access policy
689
     * @param id
690
     * @return
691
     * @throws ServiceFailure
692
     * @throws InvalidToken
693
     * @throws NotFound
694
     * @throws NotAuthorized
695
     * @throws NotImplemented
696
     * @throws InvalidRequest
697
     */
698
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
699
		Identifier pid = new Identifier();
700
		pid.setValue(id);
701
		Permission permission = null;
702
		try {
703
			String perm = params.get("action")[0];
704
			permission = Permission.convert(perm);
705
		} catch (Exception e) {
706
			logMetacat.warn("No permission specified");
707
		}
708
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
709
		response.setStatus(200);
710
		response.setContentType("text/xml");
711
		return result;
712
    }
713
    
714
    /**
715
     * Processes failed synchronization message
716
     * @throws NotImplemented
717
     * @throws ServiceFailure
718
     * @throws NotAuthorized
719
     * @throws InvalidRequest
720
     * @throws JiBXException
721
     * @throws IllegalAccessException 
722
     * @throws InstantiationException 
723
     * @throws IOException 
724
     * @throws InvalidToken 
725
     */
726
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException, InvalidToken {
727
    	SynchronizationFailed syncFailed = null;
728
		try {
729
			syncFailed = collectSynchronizationFailed();
730
		} catch (ParserConfigurationException e) {
731
			throw new ServiceFailure("2161", e.getMessage());
732
		} catch (SAXException e) {
733
			throw new ServiceFailure("2161", e.getMessage());
734
		}
735
		
736
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
737
    }
738

    
739
	/**
740
     * Calculate the checksum 
741
     * @throws NotImplemented
742
     * @throws JiBXException
743
     * @throws IOException
744
     * @throws InvalidToken
745
     * @throws ServiceFailure
746
     * @throws NotAuthorized
747
     * @throws NotFound
748
     * @throws InvalidRequest
749
     */
750
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
751
    	String checksumAlgorithm = "MD5";
752
    	try {
753
    		checksumAlgorithm = PropertyService.getProperty("dataone.checksumAlgorithm.default");
754
        } catch(Exception e) {
755
        	logMetacat.warn("Could not lookup configured default checksum algorithm, using: " + checksumAlgorithm);
756
        }    
757
        
758
        Identifier pidid = new Identifier();
759
        pidid.setValue(pid);
760
        try {
761
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
762
        } catch(Exception e) {
763
            //do nothing.  use the default
764
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
765
        }
766
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
767
        
768
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
769
        logMetacat.debug("got checksum " + c.getValue());
770
        response.setStatus(200);
771
        logMetacat.debug("serializing response");
772
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
773
        logMetacat.debug("done serializing response.");
774
        
775
    }
776
    
777
	/**
778
     * handle the replicate action for MN
779
	 * @throws JiBXException 
780
	 * @throws FileUploadException 
781
	 * @throws IOException 
782
	 * @throws InvalidRequest 
783
	 * @throws ServiceFailure 
784
	 * @throws UnsupportedType 
785
	 * @throws InsufficientResources 
786
	 * @throws NotAuthorized 
787
	 * @throws NotImplemented 
788
	 * @throws IllegalAccessException 
789
	 * @throws InstantiationException 
790
	 * @throws InvalidToken 
791
     */
792
    private void replicate() 
793
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException, 
794
        JiBXException, NotImplemented, NotAuthorized, InsufficientResources, 
795
        UnsupportedType, InstantiationException, IllegalAccessException, InvalidToken {
796

    
797
        logMetacat.debug("in POST replicate()");
798
        
799
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
800
        boolean allowed = false;
801
        if (session == null) {
802
        	String msg = "No session was provided.";
803
            NotAuthorized failure = new NotAuthorized("2152", msg);
804
        	throw failure;
805
        } else {
806
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
807
        	if (!allowed) {
808
        		String msg = "User is not an admin user";
809
                NotAuthorized failure = new NotAuthorized("2152", msg);
810
            	throw failure;
811
        	}
812
        }
813
        
814
        // parse the systemMetadata
815
        Map<String, File> files = collectMultipartFiles();        
816
        final SystemMetadata sysmeta = TypeMarshaller.unmarshalTypeFromFile(SystemMetadata.class, files.get("sysmeta"));
817
        
818
        String sn = multipartparams.get("sourceNode").get(0);
819
        logMetacat.debug("sourceNode: " + sn);
820
        final NodeReference sourceNode = new NodeReference();
821
        sourceNode.setValue(sn);
822
        
823
        // run it in a thread to avoid connection timeout
824
        Runnable runner = new Runnable() {
825
			@Override
826
			public void run() {
827
				try {
828
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
829
				} catch (Exception e) {
830
					logMetacat.error("Error running replication: " + e.getMessage(), e);
831
					throw new RuntimeException(e.getMessage(), e);
832
				}
833
			}
834
    	};
835
    	// submit the task, and that's it
836
    	executor.submit(runner);
837
        
838
    	// thread was started, so we return success
839
        response.setStatus(200);
840
        
841
    }
842

    
843
    /**
844
     * Handle the getReplica action for the MN
845
     * @param id  the identifier for the object
846
     * @throws NotFound 
847
     * @throws ServiceFailure 
848
     * @throws NotImplemented 
849
     * @throws NotAuthorized 
850
     * @throws InvalidToken 
851
     * @throws InvalidRequest 
852
     * @throws InsufficientResources 
853
     */
854
    private void getReplica(String id) 
855
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
856
        ServiceFailure, NotFound, InsufficientResources {
857
        
858
        Identifier pid = new Identifier();
859
        pid.setValue(id);
860
        OutputStream out = null;
861
        InputStream dataBytes = null;
862
                
863
        try {
864
            // call the service
865
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
866

    
867
            response.setContentType("application/octet-stream");
868
            response.setStatus(200);
869
            out = response.getOutputStream();
870
            // write the object to the output stream
871
            IOUtils.copyLarge(dataBytes, out);
872
            
873
        } catch (IOException e) {
874
            String msg = "There was an error writing the output: " + e.getMessage();
875
            logMetacat.error(msg);
876
            throw new ServiceFailure("2181", msg);
877
        
878
        }
879

    
880
    }
881

    
882
    /**
883
     * Get the Node information
884
     * 
885
     * @throws JiBXException
886
     * @throws IOException
887
     * @throws InvalidRequest 
888
     * @throws ServiceFailure 
889
     * @throws NotAuthorized 
890
     * @throws NotImplemented 
891
     */
892
    private void node() 
893
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
894
        
895
        Node n = MNodeService.getInstance(request).getCapabilities();
896
        
897
        response.setContentType("text/xml");
898
        response.setStatus(200);
899
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
900
        
901
    }
902
    
903
    /**
904
     * MN_crud.describe()
905
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
906
     * @param pid
907
     * @throws InvalidRequest 
908
     * @throws NotImplemented 
909
     * @throws NotFound 
910
     * @throws NotAuthorized 
911
     * @throws ServiceFailure 
912
     * @throws InvalidToken 
913
     */
914
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
915
    {
916
        
917
        response.setContentType("text/xml");
918

    
919
        Identifier id = new Identifier();
920
        id.setValue(pid);
921

    
922
        DescribeResponse dr = null;
923
        try {
924
        	dr = MNodeService.getInstance(request).describe(session, id);
925
        } catch (BaseException e) {
926
        	response.setStatus(e.getCode());
927
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
928
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
929
            response.addHeader("DataONE-Exception-Description", e.getDescription());
930
            response.addHeader("DataONE-Exception-PID", id.getValue());
931
            return;
932
		}
933
        
934
        response.setStatus(200);
935
        
936
        //response.addHeader("pid", pid);
937
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
938
        response.addHeader("Content-Length", dr.getContent_Length() + "");
939
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
940
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
941
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
942

    
943
        
944
    }
945
    
946
    /**
947
     * get the logs based on passed params.  Available 
948
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
949
     * for more info
950
     * @throws NotImplemented 
951
     * @throws InvalidRequest 
952
     * @throws NotAuthorized 
953
     * @throws ServiceFailure 
954
     * @throws InvalidToken 
955
     * @throws IOException 
956
     * @throws JiBXException 
957
     */
958
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
959
    {
960
            
961
        Date fromDate = null;
962
        Date toDate = null;
963
        Event event = null;
964
        Integer start = null;
965
        Integer count = null;
966
        String pidFilter = null;
967
        
968
        try {
969
        	String fromDateS = params.get("fromDate")[0];
970
            logMetacat.debug("param fromDateS: " + fromDateS);
971
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
972
        } catch (Exception e) {
973
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
974
        }
975
        try {
976
        	String toDateS = params.get("toDate")[0];
977
            logMetacat.debug("param toDateS: " + toDateS);
978
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
979
        } catch (Exception e) {
980
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
981
		}
982
        try {
983
        	String eventS = params.get("event")[0];
984
            event = Event.convert(eventS);
985
        } catch (Exception e) {
986
        	logMetacat.warn("Could not parse event: " + e.getMessage());
987
		}
988
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
989
        
990
        try {
991
        	start =  Integer.parseInt(params.get("start")[0]);
992
        } catch (Exception e) {
993
			logMetacat.warn("Could not parse start: " + e.getMessage());
994
		}
995
        try {
996
        	count =  Integer.parseInt(params.get("count")[0]);
997
        } catch (Exception e) {
998
			logMetacat.warn("Could not parse count: " + e.getMessage());
999
		}
1000
        
1001
        try {
1002
            pidFilter = params.get("pidFilter")[0];
1003
        } catch (Exception e) {
1004
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1005
        }
1006
        
1007
        logMetacat.debug("calling getLogRecords");
1008
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1009
        
1010
        OutputStream out = response.getOutputStream();
1011
        response.setStatus(200);
1012
        response.setContentType("text/xml");
1013
        
1014
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1015
        
1016
    }
1017
    
1018
    
1019
    
1020
    /**
1021
     * Implements REST version of DataONE CRUD API --> get
1022
     * @param pid ID of data object to be read
1023
     * @throws NotImplemented 
1024
     * @throws InvalidRequest 
1025
     * @throws NotFound 
1026
     * @throws NotAuthorized 
1027
     * @throws ServiceFailure 
1028
     * @throws InvalidToken 
1029
     * @throws IOException 
1030
     * @throws JiBXException 
1031
     * @throws InsufficientResources 
1032
     */
1033
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException, InsufficientResources {
1034
        OutputStream out = null;
1035
        
1036
        if (pid != null) { //get a specific document                
1037
            Identifier id = new Identifier();
1038
            id.setValue(pid);
1039
                
1040
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1041
            
1042
            // set the headers for the content
1043
            String mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1044
            if (mimeType == null) {
1045
            	mimeType = "application/octet-stream";
1046
            }
1047
            String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1048
            String filename = id.getValue();
1049
            if (extension != null) {
1050
            	filename = id.getValue() + extension;
1051
            }
1052
            response.setContentType(mimeType);
1053
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1054
            
1055
            InputStream data = MNodeService.getInstance(request).get(session, id);
1056

    
1057
            out = response.getOutputStream();  
1058
            IOUtils.copyLarge(data, out);
1059
            
1060
        }
1061
        else
1062
        { //call listObjects with specified params
1063
            Date startTime = null;
1064
            Date endTime = null;
1065
            ObjectFormatIdentifier formatId = null;
1066
            boolean replicaStatus = true;
1067
            int start = 0;
1068
            //TODO: make the max count into a const
1069
            int count = 1000;
1070
            Enumeration paramlist = request.getParameterNames();
1071
            while (paramlist.hasMoreElements()) 
1072
            { //parse the params and make the crud call
1073
                String name = (String) paramlist.nextElement();
1074
                String[] value = (String[])request.getParameterValues(name);
1075

    
1076
                if (name.equals("fromDate") && value != null)
1077
                {
1078
                    try
1079
                    {
1080
                      //startTime = dateFormat.parse(value[0]);
1081
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1082
                        //startTime = parseDateAndConvertToGMT(value[0]);
1083
                    }
1084
                    catch(Exception e)
1085
                    {  //if we can't parse it, just don't use the fromDate param
1086
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1087
                        startTime = null;
1088
                    }
1089
                }
1090
                else if(name.equals("toDate") && value != null)
1091
                {
1092
                    try
1093
                    {
1094
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1095
                    }
1096
                    catch(Exception e)
1097
                    {  //if we can't parse it, just don't use the toDate param
1098
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1099
                        endTime = null;
1100
                    }
1101
                }
1102
                else if(name.equals("formatId") && value != null) 
1103
                {
1104
                	formatId = new ObjectFormatIdentifier();
1105
                	formatId.setValue(value[0]);
1106
                }
1107
                else if(name.equals("replicaStatus") && value != null)
1108
                {
1109
                    if(value != null && 
1110
                       value.length > 0 && 
1111
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1112
                    {
1113
                        replicaStatus = false;
1114
                    }
1115
                }
1116
                else if(name.equals("start") && value != null)
1117
                {
1118
                    start = new Integer(value[0]).intValue();
1119
                }
1120
                else if(name.equals("count") && value != null)
1121
                {
1122
                    count = new Integer(value[0]).intValue();
1123
                }
1124
            }
1125
            //make the crud call
1126
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1127
                    " endTime: " + endTime + " formatId: " + 
1128
                    formatId + " replicaStatus: " + replicaStatus + 
1129
                    " start: " + start + " count: " + count);
1130
           
1131
            ObjectList ol = 
1132
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1133
           			formatId, replicaStatus, start, count);
1134
           
1135
            out = response.getOutputStream();  
1136
            response.setStatus(200);
1137
            response.setContentType("text/xml");
1138
            // Serialize and write it to the output stream
1139
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1140
            
1141
        }
1142
        
1143
    }
1144
    
1145

    
1146
    /**
1147
     * Retrieve data package as Bagit zip
1148
     * @param pid
1149
     * @throws NotImplemented 
1150
     * @throws NotFound 
1151
     * @throws NotAuthorized 
1152
     * @throws ServiceFailure 
1153
     * @throws InvalidToken 
1154
     * @throws IOException 
1155
     * @throws InvalidRequest 
1156
     */
1157
    protected void getPackage(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1158

    
1159
        Identifier id = new Identifier();
1160
        id.setValue(pid);
1161
        InputStream is = MNodeService.getInstance(request).getPackage(session, null, id);
1162
        
1163
        // use the provided filename
1164
        String filename = null;
1165
        if (is instanceof DeleteOnCloseFileInputStream) {
1166
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1167
        } else {
1168
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1169
        }
1170
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1171
        response.setContentType("application/zip");
1172
        response.setStatus(200);
1173
        OutputStream out = response.getOutputStream();
1174
        
1175
        // write it to the output stream
1176
        IOUtils.copyLarge(is, out);
1177
   }
1178
    
1179
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1180
			NotAuthorized, NotFound, NotImplemented, IOException,
1181
			JiBXException, InvalidRequest, IdentifierNotUnique,
1182
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1183

    
1184
		// publish the object
1185
		Identifier originalIdentifier = new Identifier();
1186
		originalIdentifier.setValue(pid);
1187
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1188

    
1189
		response.setStatus(200);
1190
		response.setContentType("text/xml");
1191
		OutputStream out = response.getOutputStream();
1192

    
1193
		// write new identifier to the output stream
1194
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1195
	}
1196
    
1197
    /**
1198
     * Retrieve System Metadata
1199
     * @param pid
1200
     * @throws InvalidToken
1201
     * @throws ServiceFailure
1202
     * @throws NotAuthorized
1203
     * @throws NotFound
1204
     * @throws InvalidRequest
1205
     * @throws NotImplemented
1206
     * @throws IOException
1207
     * @throws JiBXException
1208
     */
1209
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1210

    
1211
        Identifier id = new Identifier();
1212
        id.setValue(pid);
1213
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1214
        
1215
        response.setContentType("text/xml");
1216
        response.setStatus(200);
1217
        OutputStream out = response.getOutputStream();
1218
        
1219
        // Serialize and write it to the output stream
1220
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1221
   }
1222
    
1223
    
1224
    /**
1225
     * Inserts or updates the object
1226
     * 
1227
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1228
     *               is the existing pid.  If insert, the pid is the new one
1229
     * @throws InvalidRequest 
1230
     * @throws ServiceFailure 
1231
     * @throws JiBXException 
1232
     * @throws NotImplemented 
1233
     * @throws InvalidSystemMetadata 
1234
     * @throws InsufficientResources 
1235
     * @throws UnsupportedType 
1236
     * @throws IdentifierNotUnique 
1237
     * @throws NotAuthorized 
1238
     * @throws InvalidToken 
1239
     * @throws NotFound 
1240
     * @throws IOException 
1241
     * @throws IllegalAccessException 
1242
     * @throws InstantiationException 
1243
     */
1244
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1245
       
1246
    	// Read the incoming data from its Mime Multipart encoding
1247
    	Map<String, File> files = collectMultipartFiles();
1248
               
1249
    	Identifier pid = new Identifier();
1250
        if (trailingPid == null) {
1251
	        // get the pid string from the body and set the value
1252
	        String pidString = multipartparams.get("pid").get(0);
1253
	        if (pidString != null) {
1254
            pid.setValue(pidString);
1255
            
1256
          } else {
1257
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1258
              
1259
          }
1260
        } else {
1261
        	// use the pid included in the URL
1262
        	pid.setValue(trailingPid);
1263
        }
1264
        logMetacat.debug("putObject with pid " + pid.getValue());
1265
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1266

    
1267
        InputStream object = null;
1268
        InputStream sysmeta = null;
1269
        File smFile = files.get("sysmeta");
1270
        sysmeta = new FileInputStream(smFile);
1271
        File objFile = files.get("object");
1272
        object = new FileInputStream(objFile);
1273
        
1274
        // ensure we have the object bytes
1275
        if  ( objFile == null ) {
1276
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1277
            
1278
        }
1279
        
1280
        // ensure we have the system metadata
1281
        if  ( smFile == null ) {
1282
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1283
            
1284
        }
1285
        
1286
        response.setStatus(200);
1287
        response.setContentType("text/xml");
1288
        OutputStream out = response.getOutputStream();
1289
        
1290
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1291
            // handle inserts
1292
            logMetacat.debug("Commence creation...");
1293
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1294

    
1295
            logMetacat.debug("creating object with pid " + pid.getValue());
1296
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1297
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1298
            
1299
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1300
        	// handle updates
1301
        	
1302
            // construct pids
1303
            Identifier newPid = null;
1304
            try {
1305
            	String newPidString = multipartparams.get("newPid").get(0);
1306
            	newPid = new Identifier();
1307
            	newPid.setValue(newPidString);
1308
            } catch (Exception e) {
1309
				logMetacat.error("Could not get newPid from request");
1310
			}
1311
            logMetacat.debug("Commence update...");
1312
            
1313
            // get the systemmetadata object
1314
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1315

    
1316
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1317
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1318
        } else {
1319
            throw new InvalidRequest("1000", "Operation must be create or update.");
1320
        }
1321
   
1322
    }
1323

    
1324
    /**
1325
     * Handle delete 
1326
     * @param pid ID of data object to be deleted
1327
     * @throws IOException
1328
     * @throws InvalidRequest 
1329
     * @throws NotImplemented 
1330
     * @throws NotFound 
1331
     * @throws NotAuthorized 
1332
     * @throws ServiceFailure 
1333
     * @throws InvalidToken 
1334
     * @throws JiBXException 
1335
     */
1336
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1337
    {
1338

    
1339
        OutputStream out = response.getOutputStream();
1340
        response.setStatus(200);
1341
        response.setContentType("text/xml");
1342

    
1343
        Identifier id = new Identifier();
1344
        id.setValue(pid);
1345

    
1346
        logMetacat.debug("Calling delete");
1347
        MNodeService.getInstance(request).delete(session, id);
1348
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1349
        
1350
    }
1351
    
1352
    /**
1353
     * Archives the given pid
1354
     * @param pid
1355
     * @throws InvalidToken
1356
     * @throws ServiceFailure
1357
     * @throws NotAuthorized
1358
     * @throws NotFound
1359
     * @throws NotImplemented
1360
     * @throws IOException
1361
     * @throws JiBXException
1362
     */
1363
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1364

    
1365
        OutputStream out = response.getOutputStream();
1366
        response.setStatus(200);
1367
        response.setContentType("text/xml");
1368

    
1369
        Identifier id = new Identifier();
1370
        id.setValue(pid);
1371

    
1372
        logMetacat.debug("Calling archive");
1373
        MNodeService.getInstance(request).archive(session, id);
1374
        
1375
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1376
        
1377
    }
1378

    
1379
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1380
		
1381
		// Read the incoming data from its Mime Multipart encoding
1382
		logMetacat.debug("Disassembling MIME multipart form");
1383
		InputStream sf = null;
1384
	
1385
		// handle MMP inputs
1386
		File tmpDir = getTempDirectory();
1387
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1388
		MultipartRequestResolver mrr = 
1389
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1390
		MultipartRequest mr = null;
1391
		try {
1392
			mr = mrr.resolveMultipart(request);
1393
		} catch (Exception e) {
1394
			throw new ServiceFailure("2161", 
1395
					"Could not resolve multipart: " + e.getMessage());
1396
		}
1397
		logMetacat.debug("resolved multipart request");
1398
		Map<String, File> files = mr.getMultipartFiles();
1399
		if (files == null || files.keySet() == null) {
1400
			throw new InvalidRequest("2163",
1401
					"must have multipart file with name 'message'");
1402
		}
1403
		logMetacat.debug("got multipart files");
1404
	
1405
		multipartparams = mr.getMultipartParameters();
1406
	
1407
		File sfFile = files.get("message");
1408
		if (sfFile == null) {
1409
			throw new InvalidRequest("2163",
1410
					"Missing the required file-part 'message' from the multipart request.");
1411
		}
1412
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1413
		sf = new FileInputStream(sfFile);
1414
	
1415
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1416
		return syncFailed;
1417
	}
1418

    
1419
}
(3-3/4)