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: 2015-07-13 15:28:35 -0700 (Mon, 13 Jul 2015) $'
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.FileNotFoundException;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.util.Date;
32
import java.util.Enumeration;
33
import java.util.Iterator;
34
import java.util.Map;
35
import java.util.concurrent.ExecutorService;
36
import java.util.concurrent.Executors;
37

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

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

    
89
import edu.ucsb.nceas.metacat.MetaCatServlet;
90
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeInputStream;
91
import edu.ucsb.nceas.metacat.dataone.MNodeService;
92
import edu.ucsb.nceas.metacat.properties.PropertyService;
93
import edu.ucsb.nceas.metacat.restservice.D1ResourceHandler;
94
import edu.ucsb.nceas.metacat.util.DeleteOnCloseFileInputStream;
95
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
96

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

    
128
 *    systemMetadataChanged() - POST /dirtySystemMetadata/PID
129
 * 	
130
 * 	MNReplication
131
 * 		replicate() - POST /d1/mn/replicate
132
 *    getReplica() - GET /d1/mn/replica
133
 * 
134
 * ******************
135
 * @author leinfelder
136
 *
137
 */
138
public class MNResourceHandler extends D1ResourceHandler {
139

    
140
    // MN-specific API Resources
141
    protected static final String RESOURCE_MONITOR = "monitor";
142
    protected static final String RESOURCE_REPLICATE = "replicate";
143
    protected static final String RESOURCE_REPLICAS = "replica";
144
    protected static final String RESOURCE_NODE = "node";
145
    protected static final String RESOURCE_ERROR = "error";
146
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
147
    protected static final String RESOURCE_GENERATE_ID = "generate";
148
    protected static final String RESOURCE_PUBLISH = "publish";
149
    protected static final String RESOURCE_PACKAGE = "package";
150
    protected static final String RESOURCE_TOKEN = "token";
151
    protected static final String RESOURCE_WHOAMI = "whoami";
152

    
153

    
154

    
155
    
156
    // shared executor
157
	private static ExecutorService executor = null;
158

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

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

    
227
                if (resource.startsWith(RESOURCE_NODE)) {
228
                    // node response
229
                    node();
230
                    status = true;
231
                } else if (resource.startsWith(RESOURCE_TOKEN)) {
232
                    logMetacat.debug("Using resource 'token'");
233
                    // get
234
                    if (httpVerb == GET) {
235
                    	// after the command
236
                        getToken();
237
                        status = true;
238
                    }
239
                    
240
                } else if (resource.startsWith(RESOURCE_WHOAMI)) {
241
                    logMetacat.debug("Using resource 'whoami'");
242
                    // get
243
                    if (httpVerb == GET) {
244
                    	// after the command
245
                        whoami();
246
                        status = true;
247
                    }
248
                    
249
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
250
                    if (httpVerb == GET) {
251
                    	// after the command
252
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
253
	                	// check the access rules
254
	                    isAuthorized(extra);
255
	                    status = true;
256
	                    logMetacat.debug("done getting access");
257
                    }
258
                } else if (resource.startsWith(RESOURCE_META)) {
259
                    logMetacat.debug("Using resource 'meta'");
260
                    // get
261
                    if (httpVerb == GET) {
262
                        logMetacat.debug("Using resource 'meta' for GET");
263
                    	// after the command
264
                        extra = parseTrailing(resource, RESOURCE_META);
265
                        getSystemMetadataObject(extra);
266
                        status = true;
267
                    } else if (httpVerb == PUT) {
268
                        logMetacat.debug("Using resource 'meta' for PUT");
269
                        updateSystemMetadata();
270
                        status = true;
271
                    }
272
                    
273
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
274
                    logMetacat.debug("Using resource 'object'");
275
                    // after the command
276
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
277
                    logMetacat.debug("objectId: " + extra);
278
                    logMetacat.debug("verb:" + httpVerb);
279

    
280
                    if (httpVerb == GET) {
281
                        getObject(extra);
282
                        status = true;
283
                    } else if (httpVerb == POST) {
284
                    	// part of the params, not the URL
285
                        putObject(null, FUNCTION_NAME_INSERT);
286
                        status = true;
287
                    } else if (httpVerb == PUT) {
288
                        putObject(extra, FUNCTION_NAME_UPDATE);
289
                        status = true;
290
                    } else if (httpVerb == DELETE) {
291
                        deleteObject(extra);
292
                        status = true;
293
                    } else if (httpVerb == HEAD) {
294
                        describeObject(extra);
295
                        status = true;
296
                    }
297
                  
298
                } else if (resource.startsWith(RESOURCE_LOG)) {
299
                    logMetacat.debug("Using resource 'log'");
300
                    // handle log events
301
                    if (httpVerb == GET) {
302
                        getLog();
303
                        status = true;
304
                    }
305
                } else if (resource.startsWith(Constants.RESOURCE_ARCHIVE)) {
306
                    logMetacat.debug("Using resource " + Constants.RESOURCE_ARCHIVE);
307
                    // handle archive events
308
                    if (httpVerb == PUT) {
309
                        extra = parseTrailing(resource, Constants.RESOURCE_ARCHIVE);
310
                        archive(extra);
311
                        status = true;
312
                    }
313
                } else if (resource.startsWith(Constants.RESOURCE_CHECKSUM)) {
314
                    logMetacat.debug("Using resource 'checksum'");
315
                    // handle checksum requests
316
                    if (httpVerb == GET) {
317
                    	// after the command
318
                        extra = parseTrailing(resource, Constants.RESOURCE_CHECKSUM);
319
                        checksum(extra);
320
                        status = true;
321
                    }
322
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
323
                    // there are various parts to monitoring
324
                    if (httpVerb == GET) {
325
                    	// after the command
326
                        extra = parseTrailing(resource, RESOURCE_MONITOR);
327
                        
328
                        // ping
329
                        if (extra.toLowerCase().equals("ping")) {
330
                            logMetacat.debug("processing ping request");
331
                            Date result = MNodeService.getInstance(request).ping();
332
                            // TODO: send to output	
333
                            status = true;
334
                            
335
                        }
336
                        
337
                    }
338
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
339
                	if (httpVerb == POST) {
340
	                    logMetacat.debug("processing replicate request");
341
	                    replicate();
342
	                    status = true;
343
                	}
344
                } else if (resource.startsWith(RESOURCE_ERROR)) {
345
	                // sync error
346
	                if (httpVerb == POST) {
347
	                    syncError();
348
	                    status = true;
349
	                }
350
                } else if (resource.startsWith(RESOURCE_META_CHANGED)) {
351
                    // system metadata changed
352
                    if (httpVerb == POST) {
353
                        systemMetadataChanged();
354
                        status = true;
355
                    }
356
                } else if (resource.startsWith(RESOURCE_REPLICAS)) {
357
                    // get replica
358
                    if (httpVerb == GET) {
359
                        extra = parseTrailing(resource, RESOURCE_REPLICAS);
360
                        getReplica(extra);
361
                        status = true;
362
                    }
363
                } else if (resource.startsWith(RESOURCE_QUERY)) {
364
	                logMetacat.debug("Using resource " + RESOURCE_QUERY);
365
	                // after the command
366
	                extra = parseTrailing(resource, RESOURCE_QUERY);
367
	                logMetacat.debug("query extra: " + extra);
368

    
369
	                String engine = null;
370
	                String query = null;
371

    
372
	                if (extra != null) {
373
		                // get the engine
374
		                int engineIndex = extra.length();
375
		                if (extra.indexOf("/") > -1) {
376
		                	engineIndex = extra.indexOf("/");
377
		                }
378
		                engine = extra.substring(0, engineIndex);
379
		                logMetacat.debug("query engine: " + engine);
380
		                
381
		                // get the query if it is there
382
		                query = extra.substring(engineIndex, extra.length());
383
		                if (query != null && query.length() == 0) {
384
		                	query = null;
385
		                } else {
386
		                	if (query.startsWith("/")) {
387
		                		query = query.substring(1);
388
		                    }
389
		                	// remove the query delimiter if it exists
390
		                	if (query.startsWith("?")) {
391
		                		query = query.substring(1);
392
		                    }
393
		                }
394
		                logMetacat.debug("query: " + query);
395

    
396
	                }
397
	                logMetacat.debug("verb:" + httpVerb);
398
	                if (httpVerb == GET) {
399
	                    doQuery(engine, query);
400
	                    status = true;
401
	                }
402
                } else if (resource.startsWith(RESOURCE_GENERATE_ID)) {
403
                	// generate an id
404
                    if (httpVerb == POST) {
405
                        generateIdentifier();
406
                        status = true;
407
                    }
408
                } else if (resource.startsWith(RESOURCE_PUBLISH)) {
409
                    logMetacat.debug("Using resource: " + RESOURCE_PUBLISH);
410
                    // PUT
411
                    if (httpVerb == PUT) {
412
                    	// after the command
413
                        extra = parseTrailing(resource, RESOURCE_PUBLISH);
414
                        publish(extra);
415
                        status = true;
416
                    }  
417
                } else if (resource.startsWith(RESOURCE_PACKAGE)) {
418
                    logMetacat.debug("Using resource: " + RESOURCE_PACKAGE);
419
                    // after the command
420
                    extra = parseTrailing(resource, RESOURCE_PACKAGE);
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("package 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
                    
447
                    // get
448
                    if (httpVerb == GET) {
449
                        
450
                        getPackage(format, pid);
451
                        status = true;
452
                    }  
453
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
454
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
455
	                // after the command
456
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
457
	                logMetacat.debug("view extra: " + extra);
458

    
459
	                String format = null;
460
	                String pid = null;
461

    
462
	                if (extra != null) {
463
		                // get the format
464
		                int formatIndex = extra.length();
465
		                if (extra.indexOf("/") > -1) {
466
		                	formatIndex = extra.indexOf("/");
467
		                }
468
		                format = extra.substring(0, formatIndex);
469
		                logMetacat.debug("view format: " + format);
470
		                
471
		                // get the pid if it is there
472
		                pid = extra.substring(formatIndex, extra.length());
473
		                if (pid != null && pid.length() == 0) {
474
		                	pid = null;
475
		                } else {
476
		                	if (pid.startsWith("/")) {
477
		                		pid = pid.substring(1);
478
		                    }
479
		                }
480
		                logMetacat.debug("pid: " + pid);
481

    
482
	                }
483
	                logMetacat.debug("verb:" + httpVerb);
484
	                if (httpVerb == GET) {
485
	                    doViews(format, pid);
486
	                    status = true;
487
	                }
488
                } 
489
                
490
                if (!status) {
491
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
492
                }
493
            } else {
494
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
495
            }
496
        } catch (BaseException be) {
497
        	// report Exceptions as clearly as possible
498
        	OutputStream out = null;
499
			try {
500
				out = response.getOutputStream();
501
			} catch (IOException e) {
502
				logMetacat.error("Could not get output stream from response", e);
503
			}
504
            serializeException(be, out);
505
        } catch (Exception e) {
506
            // report Exceptions as clearly and generically as possible
507
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
508
        	OutputStream out = null;
509
			try {
510
				out = response.getOutputStream();
511
			} catch (IOException ioe) {
512
				logMetacat.error("Could not get output stream from response", ioe);
513
			}
514
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
515
            serializeException(se, out);
516
        }
517
    }
518
    
519

    
520
    private void doQuery(String engine, String query) {
521
    	
522
		OutputStream out = null;
523

    
524
    	try {
525
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter			
526
	    	if (engine == null) {
527
	    		// just looking for list of engines
528
	    		MNodeService mnode = MNodeService.getInstance(request);
529
    			mnode.setSession(session);
530
	    		QueryEngineList qel = mnode.listQueryEngines(session);
531
	    		response.setContentType("text/xml");
532
	            response.setStatus(200);
533
	            out = response.getOutputStream();
534
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
535
	            return;
536
	    	} else {
537
	    		if (query != null) {
538
	    			MNodeService mnode = MNodeService.getInstance(request);
539
	    			mnode.setSession(session);
540
	    			InputStream stream = mnode.query(session, engine, query);
541

    
542
	    			// set the content-type if we have it from the implementation
543
	    			if (stream instanceof ContentTypeInputStream) {
544
		    			//response.setContentType("application/octet-stream");
545
		    			//response.setContentType("text/xml");
546
	    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
547
	    			}
548
	                response.setStatus(200);
549
	                out = response.getOutputStream();
550
	                // write the results to the output stream
551
	                IOUtils.copyLarge(stream, out);
552
	                return;
553
	    		} else {
554
	    			MNodeService mnode = MNodeService.getInstance(request);
555
	    			mnode.setSession(session);
556
	    			QueryEngineDescription qed = mnode.getQueryEngineDescription(session, engine);
557
	    			response.setContentType("text/xml");
558
		            response.setStatus(200);
559
		            out = response.getOutputStream();
560
		            TypeMarshaller.marshalTypeToOutputStream(qed, out);
561
		            return;
562
	    		}
563
	    	}
564
	        
565
	        
566
    	} catch (BaseException be) {
567
        	// report Exceptions as clearly as possible
568
			try {
569
				out = response.getOutputStream();
570
			} catch (IOException e) {
571
				logMetacat.error("Could not get output stream from response", e);
572
			}
573
            serializeException(be, out);
574
        } catch (Exception e) {
575
            // report Exceptions as clearly and generically as possible
576
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
577
			try {
578
				out = response.getOutputStream();
579
			} catch (IOException ioe) {
580
				logMetacat.error("Could not get output stream from response", ioe);
581
			}
582
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
583
            serializeException(se, out);
584
        }
585
    }
586
    
587
    private void doViews(String format, String pid) {
588
    	
589
		OutputStream out = null;
590
		MNodeService mnode = MNodeService.getInstance(request);
591

    
592
    	try {
593
    		// get a list of views
594
    		if (pid != null) {
595
    			Identifier identifier = new Identifier();
596
    			identifier.setValue(pid);
597
				InputStream stream = mnode.view(session, format, identifier);
598

    
599
    			// set the content-type if we have it from the implementation
600
    			if (stream instanceof ContentTypeInputStream) {
601
    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
602
    			}
603
                response.setStatus(200);
604
                out = response.getOutputStream();
605
                // write the results to the output stream
606
                IOUtils.copyLarge(stream, out);
607
                return;
608
    		} else {
609
    			// TODO: list the registered views
610
                //BaseException ni = new NotImplemented("9999", "MN.listViews() is not implemented at this node");
611
				//throw ni;
612
    		    OptionList list = mnode.listViews(session);
613
    	        
614
    	        response.setContentType("text/xml");
615
    	        response.setStatus(200);
616
    	        TypeMarshaller.marshalTypeToOutputStream(list, response.getOutputStream());
617
    		}
618
	    	
619
	        
620
    	} catch (BaseException be) {
621
        	// report Exceptions as clearly as possible
622
			try {
623
				out = response.getOutputStream();
624
			} catch (IOException e) {
625
				logMetacat.error("Could not get output stream from response", e);
626
			}
627
            serializeException(be, out);
628
        } catch (Exception e) {
629
            // report Exceptions as clearly and generically as possible
630
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
631
			try {
632
				out = response.getOutputStream();
633
			} catch (IOException ioe) {
634
				logMetacat.error("Could not get output stream from response", ioe);
635
			}
636
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
637
            serializeException(se, out);
638
        }
639
    }
640
    
641
    /**
642
     * Handles notification of system metadata changes for the given identifier
643
     * 
644
     * @param id  the identifier for the object
645
     * @throws InvalidToken 
646
     * @throws InvalidRequest 
647
     * @throws NotAuthorized 
648
     * @throws ServiceFailure 
649
     * @throws NotImplemented 
650
     */
651
    private void systemMetadataChanged() 
652
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
653
        InvalidToken {
654

    
655
        long serialVersion = 0L;
656
        String serialVersionStr = null;
657
        Date dateSysMetaLastModified = null;
658
        String dateSysMetaLastModifiedStr = null;
659
        Identifier pid = null;
660
        
661
        // mkae sure we have the multipart params
662
        try {
663
			initMultipartParams();
664
		} catch (Exception e1) {
665
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
666
		}
667
        
668
        // get the pid
669
        try {
670
        	String id = multipartparams.get("pid").get(0);
671
        	pid = new Identifier();
672
            pid.setValue(id);            
673
        } catch (NullPointerException e) {
674
            String msg = "The 'pid' must be provided as a parameter and was not.";
675
            logMetacat.error(msg);
676
            throw new InvalidRequest("1334", msg);
677
        }      
678
        
679
        // get the serialVersion
680
        try {
681
            serialVersionStr = multipartparams.get("serialVersion").get(0);
682
            serialVersion = new Long(serialVersionStr).longValue();
683
            
684
        } catch (NullPointerException e) {
685
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
686
            logMetacat.error(msg);
687
            throw new InvalidRequest("1334", msg);
688
            
689
        }       
690
        
691
        // get the dateSysMetaLastModified
692
        try {
693
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
694
            dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
695
            
696
        } catch (NullPointerException e) {
697
            String msg = 
698
                "The 'dateSysMetaLastModified' must be provided as a " + 
699
                "parameter and was not, or was an invalid representation of the timestamp.";
700
            logMetacat.error(msg);
701
            throw new InvalidRequest("1334", msg);
702
            
703
        }       
704
        
705
        // call the service
706
        MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
707
        response.setStatus(200);
708
    }
709
    
710
    /**
711
     * Handles identifier generation calls
712
     * 
713
     * @throws InvalidRequest 
714
     * @throws NotImplemented 
715
     * @throws NotAuthorized 
716
     * @throws ServiceFailure 
717
     * @throws InvalidToken 
718
     * @throws IOException 
719
     * @throws JiBXException 
720
     */
721
    private void generateIdentifier() throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, IOException, JiBXException {
722
        
723
        // make sure we have the multipart params
724
        try {
725
			initMultipartParams();
726
		} catch (Exception e1) {
727
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
728
		}
729
        
730
        // get the scheme
731
		String scheme = null;
732
        try {
733
        	scheme = multipartparams.get("scheme").get(0);	           
734
        } catch (NullPointerException e) {
735
            String msg = "The 'scheme' parameter was not provided, using default";
736
            logMetacat.warn(msg);
737
        }
738
        
739
        // get the fragment
740
		String fragment = null;
741
        try {
742
        	fragment = multipartparams.get("fragment").get(0);	           
743
        } catch (NullPointerException e) {
744
            String msg = "The 'fragment' parameter was not provided, using default";
745
            logMetacat.warn(msg);
746
        }
747

    
748
        // call the service
749
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
750
        response.setStatus(200);
751
        response.setContentType("text/xml");
752
        OutputStream out = response.getOutputStream();
753
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
754
    }
755

    
756
    /**
757
     * Checks the access policy
758
     * @param id
759
     * @return
760
     * @throws ServiceFailure
761
     * @throws InvalidToken
762
     * @throws NotFound
763
     * @throws NotAuthorized
764
     * @throws NotImplemented
765
     * @throws InvalidRequest
766
     */
767
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
768
		Identifier pid = new Identifier();
769
		pid.setValue(id);
770
		Permission permission = null;
771
		try {
772
			String perm = params.get("action")[0];
773
			permission = Permission.convert(perm);
774
		} catch (Exception e) {
775
			logMetacat.warn("No permission specified");
776
		}
777
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
778
		response.setStatus(200);
779
		response.setContentType("text/xml");
780
		return result;
781
    }
782
    
783
    private void getToken() throws Exception {
784
		
785
		if (this.session != null) {
786
			String userId = this.session.getSubject().getValue();
787
			String fullName = null;
788
			try {
789
				Person person = this.session.getSubjectInfo().getPerson(0);
790
				fullName = person.getGivenName(0) + " " + person.getFamilyName();
791
			} catch (Exception e) {
792
				logMetacat.warn(e.getMessage(), e);
793
			}
794
			
795
			String token = null;
796
			token = TokenGenerator.getInstance().getJWT(userId, fullName);
797
			
798
			response.setStatus(200);
799
			response.setContentType("text/plain");
800
	        OutputStream out = response.getOutputStream();
801
	        out.write(token.getBytes(MetaCatServlet.DEFAULT_ENCODING));
802
	        out.close();
803
		} else {
804
			response.setStatus(401);
805
			response.setContentType("text/plain");
806
			OutputStream out = response.getOutputStream();
807
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
808
	        out.close();
809
		}
810
		
811
    }
812
    
813
    private void whoami() throws Exception {
814
		
815
		if (this.session != null) {
816
			Subject subject = this.session.getSubject();
817
			SubjectInfo subjectInfo = null;
818
			try {
819
				subjectInfo = this.session.getSubjectInfo();
820
			} catch (Exception e) {
821
				logMetacat.warn(e.getMessage(), e);
822
			}
823
			
824
			response.setStatus(200);
825
			response.setContentType("text/plain");
826
	        OutputStream out = response.getOutputStream();
827
	        
828
	        if (subjectInfo != null) {
829
		        TypeMarshaller.marshalTypeToOutputStream(subjectInfo, out);
830
	        } else {
831
		        TypeMarshaller.marshalTypeToOutputStream(subject, out);
832
	        }
833
	        
834
	        out.close();
835
		} else {
836
			response.setStatus(401);
837
			response.setContentType("text/plain");
838
			OutputStream out = response.getOutputStream();
839
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
840
	        out.close();
841
		}
842
		
843
    }
844
    
845
    /**
846
     * Processes failed synchronization message
847
     * @throws NotImplemented
848
     * @throws ServiceFailure
849
     * @throws NotAuthorized
850
     * @throws InvalidRequest
851
     * @throws JiBXException
852
     * @throws IllegalAccessException 
853
     * @throws InstantiationException 
854
     * @throws IOException 
855
     */
856
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
857
    	SynchronizationFailed syncFailed = null;
858
		try {
859
			syncFailed = collectSynchronizationFailed();
860
		} catch (ParserConfigurationException e) {
861
			throw new ServiceFailure("2161", e.getMessage());
862
		} catch (SAXException e) {
863
			throw new ServiceFailure("2161", e.getMessage());
864
		}
865
		
866
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
867
    }
868

    
869
	/**
870
     * Calculate the checksum 
871
     * @throws NotImplemented
872
     * @throws JiBXException
873
     * @throws IOException
874
     * @throws InvalidToken
875
     * @throws ServiceFailure
876
     * @throws NotAuthorized
877
     * @throws NotFound
878
     * @throws InvalidRequest
879
     */
880
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
881
    	String checksumAlgorithm = "MD5";
882
    	try {
883
    		checksumAlgorithm = PropertyService.getProperty("dataone.checksumAlgorithm.default");
884
        } catch(Exception e) {
885
        	logMetacat.warn("Could not lookup configured default checksum algorithm, using: " + checksumAlgorithm);
886
        }    
887
        
888
        Identifier pidid = new Identifier();
889
        pidid.setValue(pid);
890
        try {
891
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
892
        } catch(Exception e) {
893
            //do nothing.  use the default
894
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
895
        }
896
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
897
        
898
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
899
        logMetacat.debug("got checksum " + c.getValue());
900
        response.setStatus(200);
901
        logMetacat.debug("serializing response");
902
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
903
        logMetacat.debug("done serializing response.");
904
        
905
    }
906
    
907
	/**
908
     * handle the replicate action for MN
909
	 * @throws JiBXException 
910
	 * @throws FileUploadException 
911
	 * @throws IOException 
912
	 * @throws InvalidRequest 
913
	 * @throws ServiceFailure 
914
	 * @throws UnsupportedType 
915
	 * @throws InsufficientResources 
916
	 * @throws NotAuthorized 
917
	 * @throws NotImplemented 
918
	 * @throws IllegalAccessException 
919
	 * @throws InstantiationException 
920
	 * @throws InvalidToken 
921
     */
922
    private void replicate() 
923
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException, 
924
        JiBXException, NotImplemented, NotAuthorized, InsufficientResources, 
925
        UnsupportedType, InstantiationException, IllegalAccessException, InvalidToken {
926

    
927
        logMetacat.debug("in POST replicate()");
928
        
929
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
930
        boolean allowed = false;
931
        if (session == null) {
932
        	String msg = "No session was provided.";
933
            NotAuthorized failure = new NotAuthorized("2152", msg);
934
        	throw failure;
935
        } else {
936
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
937
        	if (!allowed) {
938
        		String msg = "User is not an admin user";
939
                NotAuthorized failure = new NotAuthorized("2152", msg);
940
            	throw failure;
941
        	}
942
        }
943
        
944
        // parse the systemMetadata
945
        Map<String, File> files = collectMultipartFiles();        
946
        final SystemMetadata sysmeta = TypeMarshaller.unmarshalTypeFromFile(SystemMetadata.class, files.get("sysmeta"));
947
        
948
        String sn = multipartparams.get("sourceNode").get(0);
949
        logMetacat.debug("sourceNode: " + sn);
950
        final NodeReference sourceNode = new NodeReference();
951
        sourceNode.setValue(sn);
952
        
953
        // run it in a thread to avoid connection timeout
954
        Runnable runner = new Runnable() {
955
			@Override
956
			public void run() {
957
				try {
958
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
959
				} catch (Exception e) {
960
					logMetacat.error("Error running replication: " + e.getMessage(), e);
961
					throw new RuntimeException(e.getMessage(), e);
962
				}
963
			}
964
    	};
965
    	// submit the task, and that's it
966
    	executor.submit(runner);
967
        
968
    	// thread was started, so we return success
969
        response.setStatus(200);
970
        
971
    }
972

    
973
    /**
974
     * Handle the getReplica action for the MN
975
     * @param id  the identifier for the object
976
     * @throws NotFound 
977
     * @throws ServiceFailure 
978
     * @throws NotImplemented 
979
     * @throws NotAuthorized 
980
     * @throws InvalidToken 
981
     * @throws InvalidRequest 
982
     */
983
    private void getReplica(String id) 
984
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
985
        ServiceFailure, NotFound {
986
        
987
        Identifier pid = new Identifier();
988
        pid.setValue(id);
989
        OutputStream out = null;
990
        InputStream dataBytes = null;
991
                
992
        try {
993
            // call the service
994
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
995

    
996
            response.setContentType("application/octet-stream");
997
            response.setStatus(200);
998
            out = response.getOutputStream();
999
            // write the object to the output stream
1000
            IOUtils.copyLarge(dataBytes, out);
1001
            
1002
        } catch (IOException e) {
1003
            String msg = "There was an error writing the output: " + e.getMessage();
1004
            logMetacat.error(msg);
1005
            throw new ServiceFailure("2181", msg);
1006
        
1007
        }
1008

    
1009
    }
1010

    
1011
    /**
1012
     * Get the Node information
1013
     * 
1014
     * @throws JiBXException
1015
     * @throws IOException
1016
     * @throws InvalidRequest 
1017
     * @throws ServiceFailure 
1018
     * @throws NotAuthorized 
1019
     * @throws NotImplemented 
1020
     */
1021
    private void node() 
1022
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
1023
        
1024
        Node n = MNodeService.getInstance(request).getCapabilities();
1025
        
1026
        response.setContentType("text/xml");
1027
        response.setStatus(200);
1028
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
1029
        
1030
    }
1031
    
1032
    /**
1033
     * MN_crud.describe()
1034
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
1035
     * @param pid
1036
     * @throws InvalidRequest 
1037
     * @throws NotImplemented 
1038
     * @throws NotFound 
1039
     * @throws NotAuthorized 
1040
     * @throws ServiceFailure 
1041
     * @throws InvalidToken 
1042
     */
1043
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
1044
    {
1045
        
1046
        response.setContentType("text/xml");
1047

    
1048
        Identifier id = new Identifier();
1049
        id.setValue(pid);
1050

    
1051
        DescribeResponse dr = null;
1052
        try {
1053
        	dr = MNodeService.getInstance(request).describe(session, id);
1054
        } catch (BaseException e) {
1055
        	response.setStatus(e.getCode());
1056
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
1057
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
1058
            response.addHeader("DataONE-Exception-Description", e.getDescription());
1059
            response.addHeader("DataONE-Exception-PID", id.getValue());
1060
            return;
1061
		}
1062
        
1063
        response.setStatus(200);
1064
        
1065
        //response.addHeader("pid", pid);
1066
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
1067
        response.addHeader("Content-Length", dr.getContent_Length() + "");
1068
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
1069
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
1070
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
1071

    
1072
        
1073
    }
1074
    
1075
    /**
1076
     * get the logs based on passed params.  Available 
1077
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
1078
     * for more info
1079
     * @throws NotImplemented 
1080
     * @throws InvalidRequest 
1081
     * @throws NotAuthorized 
1082
     * @throws ServiceFailure 
1083
     * @throws InvalidToken 
1084
     * @throws IOException 
1085
     * @throws JiBXException 
1086
     */
1087
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
1088
    {
1089
            
1090
        Date fromDate = null;
1091
        Date toDate = null;
1092
        String event = null;
1093
        Integer start = null;
1094
        Integer count = null;
1095
        String pidFilter = null;
1096
        
1097
        try {
1098
        	String fromDateS = params.get("fromDate")[0];
1099
            logMetacat.debug("param fromDateS: " + fromDateS);
1100
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1101
        } catch (Exception e) {
1102
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1103
        }
1104
        try {
1105
        	String toDateS = params.get("toDate")[0];
1106
            logMetacat.debug("param toDateS: " + toDateS);
1107
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1108
        } catch (Exception e) {
1109
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1110
		}
1111
        try {
1112
        	event = params.get("event")[0];
1113
        } catch (Exception e) {
1114
        	logMetacat.warn("Could not parse event: " + e.getMessage());
1115
		}
1116
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
1117
        
1118
        try {
1119
        	start =  Integer.parseInt(params.get("start")[0]);
1120
        } catch (Exception e) {
1121
			logMetacat.warn("Could not parse start: " + e.getMessage());
1122
		}
1123
        try {
1124
        	count =  Integer.parseInt(params.get("count")[0]);
1125
        } catch (Exception e) {
1126
			logMetacat.warn("Could not parse count: " + e.getMessage());
1127
		}
1128
        
1129
        try {
1130
            pidFilter = params.get("pidFilter")[0];
1131
        } catch (Exception e) {
1132
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1133
        }
1134
        
1135
        logMetacat.debug("calling getLogRecords");
1136
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1137
        
1138
        OutputStream out = response.getOutputStream();
1139
        response.setStatus(200);
1140
        response.setContentType("text/xml");
1141
        
1142
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1143
        
1144
    }
1145
    
1146
    
1147
    
1148
    /**
1149
     * Implements REST version of DataONE CRUD API --> get
1150
     * @param pid ID of data object to be read
1151
     * @throws NotImplemented 
1152
     * @throws InvalidRequest 
1153
     * @throws NotFound 
1154
     * @throws NotAuthorized 
1155
     * @throws ServiceFailure 
1156
     * @throws InvalidToken 
1157
     * @throws IOException 
1158
     * @throws JiBXException 
1159
     */
1160
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1161
        OutputStream out = null;
1162
        
1163
        if (pid != null) { //get a specific document                
1164
            Identifier id = new Identifier();
1165
            id.setValue(pid);
1166
                
1167
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1168
            
1169
            // set the headers for the content
1170
            String mimeType = null;
1171
            String charset = null;
1172
            ObjectFormat objectFormat = null;
1173
            
1174
            try {
1175
            	objectFormat = ObjectFormatCache.getInstance().getFormat(sm.getFormatId());
1176
        	} catch (BaseException be) {
1177
        		logMetacat.warn("Could not lookup ObjectFormat for: " + sm.getFormatId(), be);
1178
        	}
1179
            // do we have mediaType/encoding in SM?
1180
            MediaType mediaType = sm.getMediaType();
1181
            if (mediaType == null) {
1182
            	try {
1183
            		mediaType = objectFormat.getMediaType();
1184
            	} catch (Exception e) {
1185
            		logMetacat.warn("Could not lookup MediaType for: " + sm.getFormatId(), e);
1186
            	}
1187
            }
1188
            if (mediaType != null) {
1189
                mimeType = sm.getMediaType().getName();
1190
                if (sm.getMediaType().getPropertyList() != null) {
1191
                	Iterator<MediaTypeProperty> iter = sm.getMediaType().getPropertyList().iterator();
1192
                	while (iter.hasNext()) {
1193
                		MediaTypeProperty mtp = iter.next();
1194
                		if (mtp.getName().equalsIgnoreCase("charset")) {
1195
                			charset = mtp.getValue();
1196
                			mimeType += "; charset=" + charset;
1197
                			break;
1198
                		}
1199
                	}
1200
                }
1201
            }
1202
            // check object format
1203
            
1204
            // use the fallback from v1 impl
1205
            if (mimeType == null) {
1206
	            mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1207
	            
1208
	            // still null?
1209
	            if (mimeType == null) {
1210
	            	mimeType = "application/octet-stream";
1211
	            }
1212
            }
1213
            
1214
            // check for filename in SM first
1215
            String filename = sm.getFileName();
1216
            // then fallback to using id and extension
1217
            if (filename == null) {
1218
	            String extension = objectFormat.getExtension();
1219
	            if (extension == null) {
1220
	            	extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1221
	            }
1222
	            filename = id.getValue();
1223
	            if (extension != null) {
1224
	            	filename = id.getValue() + extension;
1225
	            }
1226
            }
1227
            response.setContentType(mimeType);
1228
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1229
            
1230
            InputStream data = MNodeService.getInstance(request).get(session, id);
1231

    
1232
            out = response.getOutputStream();  
1233
            IOUtils.copyLarge(data, out);
1234
            
1235
        }
1236
        else
1237
        { //call listObjects with specified params
1238
            Date startTime = null;
1239
            Date endTime = null;
1240
            ObjectFormatIdentifier formatId = null;
1241
            Identifier identifier = null;
1242
            boolean replicaStatus = true;
1243
            int start = 0;
1244
            //TODO: make the max count into a const
1245
            int count = 1000;
1246
            Enumeration paramlist = request.getParameterNames();
1247
            while (paramlist.hasMoreElements()) 
1248
            { //parse the params and make the crud call
1249
                String name = (String) paramlist.nextElement();
1250
                String[] value = (String[])request.getParameterValues(name);
1251

    
1252
                if (name.equals("fromDate") && value != null)
1253
                {
1254
                    try
1255
                    {
1256
                      //startTime = dateFormat.parse(value[0]);
1257
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1258
                        //startTime = parseDateAndConvertToGMT(value[0]);
1259
                    }
1260
                    catch(Exception e)
1261
                    {  //if we can't parse it, just don't use the fromDate param
1262
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1263
                        startTime = null;
1264
                    }
1265
                }
1266
                else if(name.equals("toDate") && value != null)
1267
                {
1268
                    try
1269
                    {
1270
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1271
                    }
1272
                    catch(Exception e)
1273
                    {  //if we can't parse it, just don't use the toDate param
1274
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1275
                        endTime = null;
1276
                    }
1277
                }
1278
                else if(name.equals("formatId") && value != null) 
1279
                {
1280
                	formatId = new ObjectFormatIdentifier();
1281
                	formatId.setValue(value[0]);
1282
                }
1283
                else if(name.equals("identifier") && value != null) 
1284
                {
1285
                	identifier = new Identifier();
1286
                	identifier.setValue(value[0]);
1287
                }
1288
                else if(name.equals("replicaStatus") && value != null)
1289
                {
1290
                    if(value != null && 
1291
                       value.length > 0 && 
1292
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1293
                    {
1294
                        replicaStatus = false;
1295
                    }
1296
                }
1297
                else if(name.equals("start") && value != null)
1298
                {
1299
                    start = new Integer(value[0]).intValue();
1300
                }
1301
                else if(name.equals("count") && value != null)
1302
                {
1303
                    count = new Integer(value[0]).intValue();
1304
                }
1305
            }
1306
            //make the crud call
1307
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1308
                    " endTime: " + endTime + " formatId: " + 
1309
                    formatId + " replicaStatus: " + replicaStatus + 
1310
                    " start: " + start + " count: " + count);
1311
           
1312
            ObjectList ol = 
1313
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1314
           			formatId, identifier, replicaStatus, start, count);
1315
           
1316
            out = response.getOutputStream();  
1317
            response.setStatus(200);
1318
            response.setContentType("text/xml");
1319
            // Serialize and write it to the output stream
1320
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1321
            
1322
        }
1323
        
1324
    }
1325
    
1326

    
1327
    /**
1328
     * Retrieve data package as Bagit zip
1329
     * @param pid
1330
     * @throws NotImplemented 
1331
     * @throws NotFound 
1332
     * @throws NotAuthorized 
1333
     * @throws ServiceFailure 
1334
     * @throws InvalidToken 
1335
     * @throws IOException 
1336
     * @throws InvalidRequest 
1337
     */
1338
    protected void getPackage(String format, String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1339

    
1340
        Identifier id = new Identifier();
1341
        id.setValue(pid);
1342
        ObjectFormatIdentifier formatId = null;
1343
        if (format != null) {
1344
        	formatId = new ObjectFormatIdentifier();
1345
        	formatId.setValue(format);
1346
        }
1347
		InputStream is = MNodeService.getInstance(request).getPackage(session, formatId , id);
1348
        
1349
        // use the provided filename
1350
        String filename = null;
1351
        if (is instanceof DeleteOnCloseFileInputStream) {
1352
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1353
        } else {
1354
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1355
        }
1356
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1357
        response.setContentType("application/zip");
1358
        response.setStatus(200);
1359
        OutputStream out = response.getOutputStream();
1360
        
1361
        // write it to the output stream
1362
        IOUtils.copyLarge(is, out);
1363
   }
1364
    
1365
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1366
			NotAuthorized, NotFound, NotImplemented, IOException,
1367
			JiBXException, InvalidRequest, IdentifierNotUnique,
1368
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1369

    
1370
		// publish the object
1371
		Identifier originalIdentifier = new Identifier();
1372
		originalIdentifier.setValue(pid);
1373
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1374

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

    
1379
		// write new identifier to the output stream
1380
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1381
	}
1382
    
1383
    /**
1384
     * Retrieve System Metadata
1385
     * @param pid
1386
     * @throws InvalidToken
1387
     * @throws ServiceFailure
1388
     * @throws NotAuthorized
1389
     * @throws NotFound
1390
     * @throws InvalidRequest
1391
     * @throws NotImplemented
1392
     * @throws IOException
1393
     * @throws JiBXException
1394
     */
1395
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1396

    
1397
        Identifier id = new Identifier();
1398
        id.setValue(pid);
1399
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1400
        
1401
        response.setContentType("text/xml");
1402
        response.setStatus(200);
1403
        OutputStream out = response.getOutputStream();
1404
        
1405
        // Serialize and write it to the output stream
1406
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1407
   }
1408
    
1409
    
1410
    /**
1411
     * Inserts or updates the object
1412
     * 
1413
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1414
     *               is the existing pid.  If insert, the pid is the new one
1415
     * @throws InvalidRequest 
1416
     * @throws ServiceFailure 
1417
     * @throws JiBXException 
1418
     * @throws NotImplemented 
1419
     * @throws InvalidSystemMetadata 
1420
     * @throws InsufficientResources 
1421
     * @throws UnsupportedType 
1422
     * @throws IdentifierNotUnique 
1423
     * @throws NotAuthorized 
1424
     * @throws InvalidToken 
1425
     * @throws NotFound 
1426
     * @throws IOException 
1427
     * @throws IllegalAccessException 
1428
     * @throws InstantiationException 
1429
     */
1430
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1431
       
1432
    	// Read the incoming data from its Mime Multipart encoding
1433
    	Map<String, File> files = collectMultipartFiles();
1434
               
1435
    	Identifier pid = new Identifier();
1436
        if (trailingPid == null) {
1437
	        // get the pid string from the body and set the value
1438
	        String pidString = multipartparams.get("pid").get(0);
1439
	        if (pidString != null) {
1440
            pid.setValue(pidString);
1441
            
1442
          } else {
1443
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1444
              
1445
          }
1446
        } else {
1447
        	// use the pid included in the URL
1448
        	pid.setValue(trailingPid);
1449
        }
1450
        logMetacat.debug("putObject with pid " + pid.getValue());
1451
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1452

    
1453
        InputStream object = null;
1454
        InputStream sysmeta = null;
1455
        File smFile = files.get("sysmeta");
1456
        sysmeta = new FileInputStream(smFile);
1457
        File objFile = files.get("object");
1458
        object = new FileInputStream(objFile);
1459
        
1460
        // ensure we have the object bytes
1461
        if  ( objFile == null ) {
1462
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1463
            
1464
        }
1465
        
1466
        // ensure we have the system metadata
1467
        if  ( smFile == null ) {
1468
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1469
            
1470
        }
1471
        
1472
        response.setStatus(200);
1473
        response.setContentType("text/xml");
1474
        OutputStream out = response.getOutputStream();
1475
        
1476
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1477
            // handle inserts
1478
            logMetacat.debug("Commence creation...");
1479
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1480

    
1481
            logMetacat.debug("creating object with pid " + pid.getValue());
1482
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1483
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1484
            
1485
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1486
        	// handle updates
1487
        	
1488
            // construct pids
1489
            Identifier newPid = null;
1490
            try {
1491
            	String newPidString = multipartparams.get("newPid").get(0);
1492
            	newPid = new Identifier();
1493
            	newPid.setValue(newPidString);
1494
            } catch (Exception e) {
1495
				logMetacat.error("Could not get newPid from request");
1496
			}
1497
            logMetacat.debug("Commence update...");
1498
            
1499
            // get the systemmetadata object
1500
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1501

    
1502
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1503
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1504
        } else {
1505
            throw new InvalidRequest("1000", "Operation must be create or update.");
1506
        }
1507
   
1508
    }
1509

    
1510
    /**
1511
     * Handle delete 
1512
     * @param pid ID of data object to be deleted
1513
     * @throws IOException
1514
     * @throws InvalidRequest 
1515
     * @throws NotImplemented 
1516
     * @throws NotFound 
1517
     * @throws NotAuthorized 
1518
     * @throws ServiceFailure 
1519
     * @throws InvalidToken 
1520
     * @throws JiBXException 
1521
     */
1522
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1523
    {
1524

    
1525
        OutputStream out = response.getOutputStream();
1526
        response.setStatus(200);
1527
        response.setContentType("text/xml");
1528

    
1529
        Identifier id = new Identifier();
1530
        id.setValue(pid);
1531

    
1532
        logMetacat.debug("Calling delete");
1533
        MNodeService.getInstance(request).delete(session, id);
1534
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1535
        
1536
    }
1537
    
1538
    /**
1539
     * Archives the given pid
1540
     * @param pid
1541
     * @throws InvalidToken
1542
     * @throws ServiceFailure
1543
     * @throws NotAuthorized
1544
     * @throws NotFound
1545
     * @throws NotImplemented
1546
     * @throws IOException
1547
     * @throws JiBXException
1548
     */
1549
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1550

    
1551
        OutputStream out = response.getOutputStream();
1552
        response.setStatus(200);
1553
        response.setContentType("text/xml");
1554

    
1555
        Identifier id = new Identifier();
1556
        id.setValue(pid);
1557

    
1558
        logMetacat.debug("Calling archive");
1559
        MNodeService.getInstance(request).archive(session, id);
1560
        
1561
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1562
        
1563
    }
1564

    
1565
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1566
		
1567
		// Read the incoming data from its Mime Multipart encoding
1568
		logMetacat.debug("Disassembling MIME multipart form");
1569
		InputStream sf = null;
1570
	
1571
		// handle MMP inputs
1572
		File tmpDir = getTempDirectory();
1573
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1574
		MultipartRequestResolver mrr = 
1575
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), MAX_UPLOAD_SIZE, 0);
1576
		MultipartRequest mr = null;
1577
		try {
1578
			mr = mrr.resolveMultipart(request);
1579
		} catch (Exception e) {
1580
			throw new ServiceFailure("2161", 
1581
					"Could not resolve multipart: " + e.getMessage());
1582
		}
1583
		logMetacat.debug("resolved multipart request");
1584
		Map<String, File> files = mr.getMultipartFiles();
1585
		if (files == null || files.keySet() == null) {
1586
			throw new InvalidRequest("2163",
1587
					"must have multipart file with name 'message'");
1588
		}
1589
		logMetacat.debug("got multipart files");
1590
	
1591
		multipartparams = mr.getMultipartParameters();
1592
	
1593
		File sfFile = files.get("message");
1594
		if (sfFile == null) {
1595
			throw new InvalidRequest("2163",
1596
					"Missing the required file-part 'message' from the multipart request.");
1597
		}
1598
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1599
		sf = new FileInputStream(sfFile);
1600
	
1601
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1602
		return syncFailed;
1603
	}
1604
	
1605
	/**
1606
	 * Update the system metadata for a specified identifier
1607
	 * @throws ServiceFailure
1608
	 * @throws InvalidRequest
1609
	 * @throws InstantiationException
1610
	 * @throws IllegalAccessException
1611
	 * @throws IOException
1612
	 * @throws JiBXException
1613
	 * @throws NotImplemented
1614
	 * @throws NotAuthorized
1615
	 * @throws InvalidSystemMetadata
1616
	 * @throws InvalidToken
1617
	 */
1618
	protected void updateSystemMetadata() throws ServiceFailure, InvalidRequest, 
1619
	                        InstantiationException, IllegalAccessException, IOException, JiBXException, NotImplemented, 
1620
	                        NotAuthorized, InvalidSystemMetadata, InvalidToken {
1621
	    // Read the incoming data from its Mime Multipart encoding
1622
        Map<String, File> files = collectMultipartFiles();
1623
        
1624
        // get the encoded pid string from the body and make the object
1625
        String pidString = multipartparams.get("pid").get(0);
1626
        Identifier pid = new Identifier();
1627
        pid.setValue(pidString);
1628
        
1629
        logMetacat.debug("updateSystemMetadata: " + pid);
1630

    
1631
        // get the system metadata from the request
1632
        File smFile = files.get("sysmeta");
1633
        FileInputStream sysmeta = new FileInputStream(smFile);
1634
        SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1635

    
1636
        logMetacat.debug("updating system metadata with pid " + pid.getValue());
1637
        
1638
        MNodeService.getInstance(request).updateSystemMetadata(session, pid, systemMetadata);
1639
	}
1640

    
1641
}
(3-3/4)