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.v2;
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.v2.Log;
64
import org.dataone.service.types.v2.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.v2.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.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
                    // after the command
383
                    extra = parseTrailing(resource, RESOURCE_PACKAGE);
384
                    
385
                    String format = null;
386
	                String pid = null;
387

    
388
	                if (extra != null) {
389
		                // get the format
390
		                int formatIndex = extra.length();
391
		                if (extra.indexOf("/") > -1) {
392
		                	formatIndex = extra.indexOf("/");
393
		                }
394
		                format = extra.substring(0, formatIndex);
395
		                logMetacat.debug("package format: " + format);
396
		                
397
		                // get the pid if it is there
398
		                pid = extra.substring(formatIndex, extra.length());
399
		                if (pid != null && pid.length() == 0) {
400
		                	pid = null;
401
		                } else {
402
		                	if (pid.startsWith("/")) {
403
		                		pid = pid.substring(1);
404
		                    }
405
		                }
406
		                logMetacat.debug("pid: " + pid);
407

    
408
	                }
409
                    
410
                    // get
411
                    if (httpVerb == GET) {
412
                        
413
                        getPackage(format, pid);
414
                        status = true;
415
                    }  
416
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
417
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
418
	                // after the command
419
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
420
	                logMetacat.debug("view extra: " + extra);
421

    
422
	                String format = null;
423
	                String pid = null;
424

    
425
	                if (extra != null) {
426
		                // get the format
427
		                int formatIndex = extra.length();
428
		                if (extra.indexOf("/") > -1) {
429
		                	formatIndex = extra.indexOf("/");
430
		                }
431
		                format = extra.substring(0, formatIndex);
432
		                logMetacat.debug("view format: " + format);
433
		                
434
		                // get the pid if it is there
435
		                pid = extra.substring(formatIndex, extra.length());
436
		                if (pid != null && pid.length() == 0) {
437
		                	pid = null;
438
		                } else {
439
		                	if (pid.startsWith("/")) {
440
		                		pid = pid.substring(1);
441
		                    }
442
		                }
443
		                logMetacat.debug("pid: " + pid);
444

    
445
	                }
446
	                logMetacat.debug("verb:" + httpVerb);
447
	                if (httpVerb == GET) {
448
	                    doViews(format, pid);
449
	                    status = true;
450
	                }
451
                } 
452
                
453
                if (!status) {
454
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
455
                }
456
            } else {
457
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
458
            }
459
        } catch (BaseException be) {
460
        	// report Exceptions as clearly as possible
461
        	OutputStream out = null;
462
			try {
463
				out = response.getOutputStream();
464
			} catch (IOException e) {
465
				logMetacat.error("Could not get output stream from response", e);
466
			}
467
            serializeException(be, out);
468
        } catch (Exception e) {
469
            // report Exceptions as clearly and generically as possible
470
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
471
        	OutputStream out = null;
472
			try {
473
				out = response.getOutputStream();
474
			} catch (IOException ioe) {
475
				logMetacat.error("Could not get output stream from response", ioe);
476
			}
477
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
478
            serializeException(se, out);
479
        }
480
    }
481
    
482

    
483
    private void doQuery(String engine, String query) {
484
    	
485
		OutputStream out = null;
486

    
487
    	try {
488
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter			
489
	    	if (engine == null) {
490
	    		// just looking for list of engines
491
	    		MNodeService mnode = MNodeService.getInstance(request);
492
    			mnode.setSession(session);
493
	    		QueryEngineList qel = mnode.listQueryEngines(session);
494
	    		response.setContentType("text/xml");
495
	            response.setStatus(200);
496
	            out = response.getOutputStream();
497
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
498
	            return;
499
	    	} else {
500
	    		if (query != null) {
501
	    			MNodeService mnode = MNodeService.getInstance(request);
502
	    			mnode.setSession(session);
503
	    			InputStream stream = mnode.query(session, engine, query);
504

    
505
	    			// set the content-type if we have it from the implementation
506
	    			if (stream instanceof ContentTypeInputStream) {
507
		    			//response.setContentType("application/octet-stream");
508
		    			//response.setContentType("text/xml");
509
	    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
510
	    			}
511
	                response.setStatus(200);
512
	                out = response.getOutputStream();
513
	                // write the results to the output stream
514
	                IOUtils.copyLarge(stream, out);
515
	                return;
516
	    		} else {
517
	    			MNodeService mnode = MNodeService.getInstance(request);
518
	    			mnode.setSession(session);
519
	    			QueryEngineDescription qed = mnode.getQueryEngineDescription(session, engine);
520
	    			response.setContentType("text/xml");
521
		            response.setStatus(200);
522
		            out = response.getOutputStream();
523
		            TypeMarshaller.marshalTypeToOutputStream(qed, out);
524
		            return;
525
	    		}
526
	    	}
527
	        
528
	        
529
    	} catch (BaseException be) {
530
        	// report Exceptions as clearly as possible
531
			try {
532
				out = response.getOutputStream();
533
			} catch (IOException e) {
534
				logMetacat.error("Could not get output stream from response", e);
535
			}
536
            serializeException(be, out);
537
        } catch (Exception e) {
538
            // report Exceptions as clearly and generically as possible
539
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
540
			try {
541
				out = response.getOutputStream();
542
			} catch (IOException ioe) {
543
				logMetacat.error("Could not get output stream from response", ioe);
544
			}
545
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
546
            serializeException(se, out);
547
        }
548
    }
549
    
550
    private void doViews(String format, String pid) {
551
    	
552
		OutputStream out = null;
553
		MNodeService mnode = MNodeService.getInstance(request);
554

    
555
    	try {
556
    		// get a list of views
557
    		if (pid != null) {
558
    			Identifier identifier = new Identifier();
559
    			identifier.setValue(pid);
560
				InputStream stream = mnode.view(session, format, identifier);
561

    
562
    			// set the content-type if we have it from the implementation
563
    			if (stream instanceof ContentTypeInputStream) {
564
    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
565
    			}
566
                response.setStatus(200);
567
                out = response.getOutputStream();
568
                // write the results to the output stream
569
                IOUtils.copyLarge(stream, out);
570
                return;
571
    		} else {
572
    			// TODO: list the registered views
573
                BaseException ni = new NotImplemented("9999", "MN.listViews() is not implemented at this node");
574
				throw ni;
575
    		}
576
	    	
577
	        
578
    	} catch (BaseException be) {
579
        	// report Exceptions as clearly as possible
580
			try {
581
				out = response.getOutputStream();
582
			} catch (IOException e) {
583
				logMetacat.error("Could not get output stream from response", e);
584
			}
585
            serializeException(be, out);
586
        } catch (Exception e) {
587
            // report Exceptions as clearly and generically as possible
588
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
589
			try {
590
				out = response.getOutputStream();
591
			} catch (IOException ioe) {
592
				logMetacat.error("Could not get output stream from response", ioe);
593
			}
594
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
595
            serializeException(se, out);
596
        }
597
    }
598
    
599
    /**
600
     * Handles notification of system metadata changes for the given identifier
601
     * 
602
     * @param id  the identifier for the object
603
     * @throws InvalidToken 
604
     * @throws InvalidRequest 
605
     * @throws NotAuthorized 
606
     * @throws ServiceFailure 
607
     * @throws NotImplemented 
608
     */
609
    private void systemMetadataChanged() 
610
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
611
        InvalidToken {
612

    
613
        long serialVersion = 0L;
614
        String serialVersionStr = null;
615
        Date dateSysMetaLastModified = null;
616
        String dateSysMetaLastModifiedStr = null;
617
        Identifier pid = null;
618
        
619
        // mkae sure we have the multipart params
620
        try {
621
			initMultipartParams();
622
		} catch (Exception e1) {
623
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
624
		}
625
        
626
        // get the pid
627
        try {
628
        	String id = multipartparams.get("pid").get(0);
629
        	pid = new Identifier();
630
            pid.setValue(id);            
631
        } catch (NullPointerException e) {
632
            String msg = "The 'pid' must be provided as a parameter and was not.";
633
            logMetacat.error(msg);
634
            throw new InvalidRequest("1334", msg);
635
        }      
636
        
637
        // get the serialVersion
638
        try {
639
            serialVersionStr = multipartparams.get("serialVersion").get(0);
640
            serialVersion = new Long(serialVersionStr).longValue();
641
            
642
        } catch (NullPointerException e) {
643
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
644
            logMetacat.error(msg);
645
            throw new InvalidRequest("1334", msg);
646
            
647
        }       
648
        
649
        // get the dateSysMetaLastModified
650
        try {
651
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
652
            dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
653
            
654
        } catch (NullPointerException e) {
655
            String msg = 
656
                "The 'dateSysMetaLastModified' must be provided as a " + 
657
                "parameter and was not, or was an invalid representation of the timestamp.";
658
            logMetacat.error(msg);
659
            throw new InvalidRequest("1334", msg);
660
            
661
        }       
662
        
663
        // call the service
664
        MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
665
        response.setStatus(200);
666
    }
667
    
668
    /**
669
     * Handles identifier generation calls
670
     * 
671
     * @throws InvalidRequest 
672
     * @throws NotImplemented 
673
     * @throws NotAuthorized 
674
     * @throws ServiceFailure 
675
     * @throws InvalidToken 
676
     * @throws IOException 
677
     * @throws JiBXException 
678
     */
679
    private void generateIdentifier() throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, IOException, JiBXException {
680
        
681
        // make sure we have the multipart params
682
        try {
683
			initMultipartParams();
684
		} catch (Exception e1) {
685
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
686
		}
687
        
688
        // get the scheme
689
		String scheme = null;
690
        try {
691
        	scheme = multipartparams.get("scheme").get(0);	           
692
        } catch (NullPointerException e) {
693
            String msg = "The 'scheme' parameter was not provided, using default";
694
            logMetacat.warn(msg);
695
        }
696
        
697
        // get the fragment
698
		String fragment = null;
699
        try {
700
        	fragment = multipartparams.get("fragment").get(0);	           
701
        } catch (NullPointerException e) {
702
            String msg = "The 'fragment' parameter was not provided, using default";
703
            logMetacat.warn(msg);
704
        }
705

    
706
        // call the service
707
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
708
        response.setStatus(200);
709
        response.setContentType("text/xml");
710
        OutputStream out = response.getOutputStream();
711
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
712
    }
713

    
714
    /**
715
     * Checks the access policy
716
     * @param id
717
     * @return
718
     * @throws ServiceFailure
719
     * @throws InvalidToken
720
     * @throws NotFound
721
     * @throws NotAuthorized
722
     * @throws NotImplemented
723
     * @throws InvalidRequest
724
     */
725
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
726
		Identifier pid = new Identifier();
727
		pid.setValue(id);
728
		Permission permission = null;
729
		try {
730
			String perm = params.get("action")[0];
731
			permission = Permission.convert(perm);
732
		} catch (Exception e) {
733
			logMetacat.warn("No permission specified");
734
		}
735
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
736
		response.setStatus(200);
737
		response.setContentType("text/xml");
738
		return result;
739
    }
740
    
741
    /**
742
     * Processes failed synchronization message
743
     * @throws NotImplemented
744
     * @throws ServiceFailure
745
     * @throws NotAuthorized
746
     * @throws InvalidRequest
747
     * @throws JiBXException
748
     * @throws IllegalAccessException 
749
     * @throws InstantiationException 
750
     * @throws IOException 
751
     */
752
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
753
    	SynchronizationFailed syncFailed = null;
754
		try {
755
			syncFailed = collectSynchronizationFailed();
756
		} catch (ParserConfigurationException e) {
757
			throw new ServiceFailure("2161", e.getMessage());
758
		} catch (SAXException e) {
759
			throw new ServiceFailure("2161", e.getMessage());
760
		}
761
		
762
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
763
    }
764

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

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

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

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

    
905
    }
906

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

    
944
        Identifier id = new Identifier();
945
        id.setValue(pid);
946

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

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

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

    
1100
                if (name.equals("fromDate") && value != null)
1101
                {
1102
                    try
1103
                    {
1104
                      //startTime = dateFormat.parse(value[0]);
1105
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1106
                        //startTime = parseDateAndConvertToGMT(value[0]);
1107
                    }
1108
                    catch(Exception e)
1109
                    {  //if we can't parse it, just don't use the fromDate param
1110
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1111
                        startTime = null;
1112
                    }
1113
                }
1114
                else if(name.equals("toDate") && value != null)
1115
                {
1116
                    try
1117
                    {
1118
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1119
                    }
1120
                    catch(Exception e)
1121
                    {  //if we can't parse it, just don't use the toDate param
1122
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1123
                        endTime = null;
1124
                    }
1125
                }
1126
                else if(name.equals("formatId") && value != null) 
1127
                {
1128
                	formatId = new ObjectFormatIdentifier();
1129
                	formatId.setValue(value[0]);
1130
                }
1131
                else if(name.equals("identifier") && value != null) 
1132
                {
1133
                	identifier = new Identifier();
1134
                	identifier.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, identifier, 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 data package as Bagit zip
1177
     * @param pid
1178
     * @throws NotImplemented 
1179
     * @throws NotFound 
1180
     * @throws NotAuthorized 
1181
     * @throws ServiceFailure 
1182
     * @throws InvalidToken 
1183
     * @throws IOException 
1184
     * @throws InvalidRequest 
1185
     */
1186
    protected void getPackage(String format, String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1187

    
1188
        Identifier id = new Identifier();
1189
        id.setValue(pid);
1190
        ObjectFormatIdentifier formatId = null;
1191
        if (format != null) {
1192
        	formatId = new ObjectFormatIdentifier();
1193
        	formatId.setValue(format);
1194
        }
1195
		InputStream is = MNodeService.getInstance(request).getPackage(session, formatId , id);
1196
        
1197
        // use the provided filename
1198
        String filename = null;
1199
        if (is instanceof DeleteOnCloseFileInputStream) {
1200
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1201
        } else {
1202
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1203
        }
1204
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1205
        response.setContentType("application/zip");
1206
        response.setStatus(200);
1207
        OutputStream out = response.getOutputStream();
1208
        
1209
        // write it to the output stream
1210
        IOUtils.copyLarge(is, out);
1211
   }
1212
    
1213
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1214
			NotAuthorized, NotFound, NotImplemented, IOException,
1215
			JiBXException, InvalidRequest, IdentifierNotUnique,
1216
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1217

    
1218
		// publish the object
1219
		Identifier originalIdentifier = new Identifier();
1220
		originalIdentifier.setValue(pid);
1221
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1222

    
1223
		response.setStatus(200);
1224
		response.setContentType("text/xml");
1225
		OutputStream out = response.getOutputStream();
1226

    
1227
		// write new identifier to the output stream
1228
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1229
	}
1230
    
1231
    /**
1232
     * Retrieve System Metadata
1233
     * @param pid
1234
     * @throws InvalidToken
1235
     * @throws ServiceFailure
1236
     * @throws NotAuthorized
1237
     * @throws NotFound
1238
     * @throws InvalidRequest
1239
     * @throws NotImplemented
1240
     * @throws IOException
1241
     * @throws JiBXException
1242
     */
1243
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1244

    
1245
        Identifier id = new Identifier();
1246
        id.setValue(pid);
1247
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1248
        
1249
        response.setContentType("text/xml");
1250
        response.setStatus(200);
1251
        OutputStream out = response.getOutputStream();
1252
        
1253
        // Serialize and write it to the output stream
1254
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1255
   }
1256
    
1257
    
1258
    /**
1259
     * Inserts or updates the object
1260
     * 
1261
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1262
     *               is the existing pid.  If insert, the pid is the new one
1263
     * @throws InvalidRequest 
1264
     * @throws ServiceFailure 
1265
     * @throws JiBXException 
1266
     * @throws NotImplemented 
1267
     * @throws InvalidSystemMetadata 
1268
     * @throws InsufficientResources 
1269
     * @throws UnsupportedType 
1270
     * @throws IdentifierNotUnique 
1271
     * @throws NotAuthorized 
1272
     * @throws InvalidToken 
1273
     * @throws NotFound 
1274
     * @throws IOException 
1275
     * @throws IllegalAccessException 
1276
     * @throws InstantiationException 
1277
     */
1278
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1279
       
1280
    	// Read the incoming data from its Mime Multipart encoding
1281
    	Map<String, File> files = collectMultipartFiles();
1282
               
1283
    	Identifier pid = new Identifier();
1284
        if (trailingPid == null) {
1285
	        // get the pid string from the body and set the value
1286
	        String pidString = multipartparams.get("pid").get(0);
1287
	        if (pidString != null) {
1288
            pid.setValue(pidString);
1289
            
1290
          } else {
1291
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1292
              
1293
          }
1294
        } else {
1295
        	// use the pid included in the URL
1296
        	pid.setValue(trailingPid);
1297
        }
1298
        logMetacat.debug("putObject with pid " + pid.getValue());
1299
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1300

    
1301
        InputStream object = null;
1302
        InputStream sysmeta = null;
1303
        File smFile = files.get("sysmeta");
1304
        sysmeta = new FileInputStream(smFile);
1305
        File objFile = files.get("object");
1306
        object = new FileInputStream(objFile);
1307
        
1308
        // ensure we have the object bytes
1309
        if  ( objFile == null ) {
1310
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1311
            
1312
        }
1313
        
1314
        // ensure we have the system metadata
1315
        if  ( smFile == null ) {
1316
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1317
            
1318
        }
1319
        
1320
        response.setStatus(200);
1321
        response.setContentType("text/xml");
1322
        OutputStream out = response.getOutputStream();
1323
        
1324
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1325
            // handle inserts
1326
            logMetacat.debug("Commence creation...");
1327
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1328

    
1329
            logMetacat.debug("creating object with pid " + pid.getValue());
1330
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1331
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1332
            
1333
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1334
        	// handle updates
1335
        	
1336
            // construct pids
1337
            Identifier newPid = null;
1338
            try {
1339
            	String newPidString = multipartparams.get("newPid").get(0);
1340
            	newPid = new Identifier();
1341
            	newPid.setValue(newPidString);
1342
            } catch (Exception e) {
1343
				logMetacat.error("Could not get newPid from request");
1344
			}
1345
            logMetacat.debug("Commence update...");
1346
            
1347
            // get the systemmetadata object
1348
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1349

    
1350
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1351
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1352
        } else {
1353
            throw new InvalidRequest("1000", "Operation must be create or update.");
1354
        }
1355
   
1356
    }
1357

    
1358
    /**
1359
     * Handle delete 
1360
     * @param pid ID of data object to be deleted
1361
     * @throws IOException
1362
     * @throws InvalidRequest 
1363
     * @throws NotImplemented 
1364
     * @throws NotFound 
1365
     * @throws NotAuthorized 
1366
     * @throws ServiceFailure 
1367
     * @throws InvalidToken 
1368
     * @throws JiBXException 
1369
     */
1370
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1371
    {
1372

    
1373
        OutputStream out = response.getOutputStream();
1374
        response.setStatus(200);
1375
        response.setContentType("text/xml");
1376

    
1377
        Identifier id = new Identifier();
1378
        id.setValue(pid);
1379

    
1380
        logMetacat.debug("Calling delete");
1381
        MNodeService.getInstance(request).delete(session, id);
1382
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1383
        
1384
    }
1385
    
1386
    /**
1387
     * Archives the given pid
1388
     * @param pid
1389
     * @throws InvalidToken
1390
     * @throws ServiceFailure
1391
     * @throws NotAuthorized
1392
     * @throws NotFound
1393
     * @throws NotImplemented
1394
     * @throws IOException
1395
     * @throws JiBXException
1396
     */
1397
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1398

    
1399
        OutputStream out = response.getOutputStream();
1400
        response.setStatus(200);
1401
        response.setContentType("text/xml");
1402

    
1403
        Identifier id = new Identifier();
1404
        id.setValue(pid);
1405

    
1406
        logMetacat.debug("Calling archive");
1407
        MNodeService.getInstance(request).archive(session, id);
1408
        
1409
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1410
        
1411
    }
1412

    
1413
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1414
		
1415
		// Read the incoming data from its Mime Multipart encoding
1416
		logMetacat.debug("Disassembling MIME multipart form");
1417
		InputStream sf = null;
1418
	
1419
		// handle MMP inputs
1420
		File tmpDir = getTempDirectory();
1421
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1422
		MultipartRequestResolver mrr = 
1423
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1424
		MultipartRequest mr = null;
1425
		try {
1426
			mr = mrr.resolveMultipart(request);
1427
		} catch (Exception e) {
1428
			throw new ServiceFailure("2161", 
1429
					"Could not resolve multipart: " + e.getMessage());
1430
		}
1431
		logMetacat.debug("resolved multipart request");
1432
		Map<String, File> files = mr.getMultipartFiles();
1433
		if (files == null || files.keySet() == null) {
1434
			throw new InvalidRequest("2163",
1435
					"must have multipart file with name 'message'");
1436
		}
1437
		logMetacat.debug("got multipart files");
1438
	
1439
		multipartparams = mr.getMultipartParameters();
1440
	
1441
		File sfFile = files.get("message");
1442
		if (sfFile == null) {
1443
			throw new InvalidRequest("2163",
1444
					"Missing the required file-part 'message' from the multipart request.");
1445
		}
1446
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1447
		sf = new FileInputStream(sfFile);
1448
	
1449
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1450
		return syncFailed;
1451
	}
1452

    
1453
}
(3-3/4)