Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: leinfelder $'
7
 *     '$Date: 2013-07-09 10:40:21 -0700 (Tue, 09 Jul 2013) $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.restservice;
24

    
25
import java.io.File;
26
import java.io.FileInputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
30
import java.text.DateFormat;
31
import java.text.ParseException;
32
import java.text.SimpleDateFormat;
33
import java.util.Date;
34
import java.util.Enumeration;
35
import java.util.Map;
36
import java.util.TimeZone;
37
import java.util.concurrent.ExecutorService;
38
import java.util.concurrent.Executors;
39

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

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

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

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

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

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

    
146

    
147
    
148
    // shared executor
149
	private static ExecutorService executor = null;
150

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

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

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

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

    
341
	                String engine = null;
342
	                String query = null;
343

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

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

    
400
	                String format = null;
401
	                String pid = null;
402

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

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

    
461
    private void doQuery(String engine, String query) {
462
    	
463
		OutputStream out = null;
464

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

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

    
533
    	try {
534
    		// get a list of views
535
    		if (pid != null) {
536
    			Identifier identifier = new Identifier();
537
    			identifier.setValue(pid);
538
				InputStream stream = mnode.getView(session, identifier, format);
539

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

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

    
684
        // call the service
685
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
686
        response.setStatus(200);
687
        response.setContentType("text/xml");
688
        OutputStream out = response.getOutputStream();
689
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
690
    }
691

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

    
744
    /**
745
     * Handles the monitoring resources
746
     * @return
747
     * @throws NotFound
748
     * @throws ParseException
749
     * @throws NotImplemented
750
     * @throws ServiceFailure
751
     * @throws NotAuthorized
752
     * @throws InvalidRequest
753
     * @throws InsufficientResources
754
     * @throws UnsupportedType
755
     * @throws IOException
756
     * @throws JiBXException
757
     */
758
    private boolean monitor(String pathInfo) 
759
      throws NotFound, ParseException, NotImplemented, ServiceFailure, 
760
      NotAuthorized, InvalidRequest, InsufficientResources, UnsupportedType, 
761
      IOException, JiBXException {
762
    	logMetacat.debug("processing monitor request");
763
        
764
        logMetacat.debug("verb is GET");
765
        logMetacat.debug("pathInfo is " + pathInfo);
766
        
767
        if (pathInfo.toLowerCase().equals("status")) {
768
            logMetacat.debug("processing status request");
769
            // TODO: implement in MNCore
770
            //MNodeService.getInstance().getStatus();
771
            return false;
772
            
773
        } else if (pathInfo.toLowerCase().equals("object")) {
774
            logMetacat.debug("processing object request");
775
            Identifier pid = null;
776
            ObjectFormat format = null;
777
            if (params.containsKey("format")) {
778
                String f = params.get("format")[0];
779
                format = ObjectFormatCache.getInstance().getFormat(f);
780
            }
781
            if (params.containsKey("pid")) {
782
                String id = params.get("pid")[0];
783
                pid = new Identifier();
784
                pid.setValue(id);
785
            }
786
            
787
            // TODO: implement in MNCore
788
            //ObjectStatistics objectStats = MNodeService.getInstance().getObjectStatistics(format, pid);
789
            return false;
790
            
791
        } else if (pathInfo.toLowerCase().equals("event")) {
792
            logMetacat.debug("processing event request");
793
            ObjectFormatIdentifier fmtid = null;
794
            String fromDateStr = null;
795
            Date fromDate = null;
796
            String toDateStr = null;
797
            Date toDate = null;
798
            String requestor = null;
799
            Subject subject = null;
800
            String eventName = null;
801
            Event event = null;
802

    
803
            if (params.containsKey("formatId")) {
804
                String f = params.get("formatId")[0];
805
                fmtid = ObjectFormatCache.getInstance().getFormat(f).getFormatId();
806
            }
807
            
808
            if (params.containsKey("fromDate")) {
809
                fromDateStr = params.get("fromDate")[0];
810
                fromDate = getDateAsUTC(fromDateStr);
811
            }
812
            
813
            if (params.containsKey("toDate")) {
814
              toDateStr = params.get("toDate")[0];
815
              toDate = getDateAsUTC(toDateStr);
816
            }
817
            
818
            if (params.containsKey("requestor")) {
819
            	requestor = params.get("requestor")[0];
820
            	subject = new Subject();
821
            	subject.setValue(requestor);
822
            }
823
            
824
            if (params.containsKey("event")) {
825
            	eventName = params.get("event")[0];
826
                event = Event.convert(eventName);
827
            }
828
            
829
            MonitorList monitorList = MNodeService.getInstance(request).getOperationStatistics(session, fromDate, toDate, subject, event, fmtid);
830
            
831
            OutputStream out = response.getOutputStream();
832
            response.setStatus(200);
833
            response.setContentType("text/xml");
834
            
835
            TypeMarshaller.marshalTypeToOutputStream(monitorList, out);
836
            
837
            return true;
838
            
839
        }
840
        
841
        return false;
842
    }
843
    
844
    /*
845
     * Parse an ISO8601 date string, returning a Date in UTC time if the string
846
     * ends in 'Z'.
847
     * 
848
     * @param fromDateStr -  the date string to be parsed
849
     * @return date -  the date object represented by the string
850
     */
851
    private Date getDateAsUTC(String fromDateStr)
852
      throws ParseException {
853

    
854
    	Date date = null;
855
    	
856
    	try {
857
    		// try the expected date format
858
        // a date format for date string arguments
859
        DateFormat dateFormat = 
860
        	new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
861

    
862
	      date = dateFormat.parse(fromDateStr);
863
      
864
    	} catch (ParseException e) {
865
    		// try the date with the UTC indicator
866
        DateFormat utcDateFormat = 
867
        	new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
868
        utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
869
        date = utcDateFormat.parse(fromDateStr);
870
        
871
      }
872
    	
873
    	return date;
874
    }
875

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

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

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

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

    
1015
    }
1016

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

    
1054
        Identifier id = new Identifier();
1055
        id.setValue(pid);
1056

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

    
1078
        
1079
    }
1080
    
1081
    /**
1082
     * get the logs based on passed params.  Available 
1083
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
1084
     * for more info
1085
     * @throws NotImplemented 
1086
     * @throws InvalidRequest 
1087
     * @throws NotAuthorized 
1088
     * @throws ServiceFailure 
1089
     * @throws InvalidToken 
1090
     * @throws IOException 
1091
     * @throws JiBXException 
1092
     */
1093
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
1094
    {
1095
            
1096
        Date fromDate = null;
1097
        Date toDate = null;
1098
        Event event = null;
1099
        Integer start = null;
1100
        Integer count = null;
1101
        String pidFilter = null;
1102
        
1103
        try {
1104
        	String fromDateS = params.get("fromDate")[0];
1105
            logMetacat.debug("param fromDateS: " + fromDateS);
1106
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1107
        } catch (Exception e) {
1108
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1109
        }
1110
        try {
1111
        	String toDateS = params.get("toDate")[0];
1112
            logMetacat.debug("param toDateS: " + toDateS);
1113
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1114
        } catch (Exception e) {
1115
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1116
		}
1117
        try {
1118
        	String eventS = params.get("event")[0];
1119
            event = Event.convert(eventS);
1120
        } catch (Exception e) {
1121
        	logMetacat.warn("Could not parse event: " + e.getMessage());
1122
		}
1123
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
1124
        
1125
        try {
1126
        	start =  Integer.parseInt(params.get("start")[0]);
1127
        } catch (Exception e) {
1128
			logMetacat.warn("Could not parse start: " + e.getMessage());
1129
		}
1130
        try {
1131
        	count =  Integer.parseInt(params.get("count")[0]);
1132
        } catch (Exception e) {
1133
			logMetacat.warn("Could not parse count: " + e.getMessage());
1134
		}
1135
        
1136
        try {
1137
            pidFilter = params.get("pidFilter")[0];
1138
        } catch (Exception e) {
1139
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1140
        }
1141
        
1142
        logMetacat.debug("calling getLogRecords");
1143
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1144
        
1145
        OutputStream out = response.getOutputStream();
1146
        response.setStatus(200);
1147
        response.setContentType("text/xml");
1148
        
1149
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1150
        
1151
    }
1152
    
1153
    
1154
    
1155
    /**
1156
     * Implements REST version of DataONE CRUD API --> get
1157
     * @param pid ID of data object to be read
1158
     * @throws NotImplemented 
1159
     * @throws InvalidRequest 
1160
     * @throws NotFound 
1161
     * @throws NotAuthorized 
1162
     * @throws ServiceFailure 
1163
     * @throws InvalidToken 
1164
     * @throws IOException 
1165
     * @throws JiBXException 
1166
     */
1167
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1168
        OutputStream out = null;
1169
        
1170
        if (pid != null) { //get a specific document                
1171
            Identifier id = new Identifier();
1172
            id.setValue(pid);
1173
                
1174
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1175
            
1176
            // set the headers for the content
1177
            String mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1178
            if (mimeType == null) {
1179
            	mimeType = "application/octet-stream";
1180
            }
1181
            String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1182
            String filename = id.getValue();
1183
            if (extension != null) {
1184
            	filename = id.getValue() + extension;
1185
            }
1186
            response.setContentType(mimeType);
1187
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1188
            
1189
            InputStream data = MNodeService.getInstance(request).get(session, id);
1190

    
1191
            out = response.getOutputStream();  
1192
            IOUtils.copyLarge(data, out);
1193
            
1194
        }
1195
        else
1196
        { //call listObjects with specified params
1197
            Date startTime = null;
1198
            Date endTime = null;
1199
            ObjectFormatIdentifier formatId = null;
1200
            boolean replicaStatus = true;
1201
            int start = 0;
1202
            //TODO: make the max count into a const
1203
            int count = 1000;
1204
            Enumeration paramlist = request.getParameterNames();
1205
            while (paramlist.hasMoreElements()) 
1206
            { //parse the params and make the crud call
1207
                String name = (String) paramlist.nextElement();
1208
                String[] value = (String[])request.getParameterValues(name);
1209

    
1210
                if (name.equals("fromDate") && value != null)
1211
                {
1212
                    try
1213
                    {
1214
                      //startTime = dateFormat.parse(value[0]);
1215
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1216
                        //startTime = parseDateAndConvertToGMT(value[0]);
1217
                    }
1218
                    catch(Exception e)
1219
                    {  //if we can't parse it, just don't use the fromDate param
1220
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1221
                        startTime = null;
1222
                    }
1223
                }
1224
                else if(name.equals("toDate") && value != null)
1225
                {
1226
                    try
1227
                    {
1228
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1229
                    }
1230
                    catch(Exception e)
1231
                    {  //if we can't parse it, just don't use the toDate param
1232
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1233
                        endTime = null;
1234
                    }
1235
                }
1236
                else if(name.equals("formatId") && value != null) 
1237
                {
1238
                	formatId = new ObjectFormatIdentifier();
1239
                	formatId.setValue(value[0]);
1240
                }
1241
                else if(name.equals("replicaStatus") && value != null)
1242
                {
1243
                    if(value != null && 
1244
                       value.length > 0 && 
1245
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1246
                    {
1247
                        replicaStatus = false;
1248
                    }
1249
                }
1250
                else if(name.equals("start") && value != null)
1251
                {
1252
                    start = new Integer(value[0]).intValue();
1253
                }
1254
                else if(name.equals("count") && value != null)
1255
                {
1256
                    count = new Integer(value[0]).intValue();
1257
                }
1258
            }
1259
            //make the crud call
1260
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1261
                    " endTime: " + endTime + " formatId: " + 
1262
                    formatId + " replicaStatus: " + replicaStatus + 
1263
                    " start: " + start + " count: " + count);
1264
           
1265
            ObjectList ol = 
1266
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1267
           			formatId, replicaStatus, start, count);
1268
           
1269
            out = response.getOutputStream();  
1270
            response.setStatus(200);
1271
            response.setContentType("text/xml");
1272
            // Serialize and write it to the output stream
1273
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1274
            
1275
        }
1276
        
1277
    }
1278
    
1279

    
1280
    /**
1281
     * Retrieve data package as Bagit zip
1282
     * @param pid
1283
     * @throws NotImplemented 
1284
     * @throws NotFound 
1285
     * @throws NotAuthorized 
1286
     * @throws ServiceFailure 
1287
     * @throws InvalidToken 
1288
     * @throws IOException 
1289
     */
1290
    protected void getPackage(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException {
1291

    
1292
        Identifier id = new Identifier();
1293
        id.setValue(pid);
1294
        InputStream is = MNodeService.getInstance(request).getPackage(session, id);
1295
        
1296
        response.setContentType("application/zip");
1297
        response.setStatus(200);
1298
        OutputStream out = response.getOutputStream();
1299
        
1300
        // write it to the output stream
1301
        IOUtils.copyLarge(is, out);
1302
   }
1303
    
1304
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1305
			NotAuthorized, NotFound, NotImplemented, IOException,
1306
			JiBXException, InvalidRequest, IdentifierNotUnique,
1307
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1308

    
1309
		// publish the object
1310
		Identifier originalIdentifier = new Identifier();
1311
		originalIdentifier.setValue(pid);
1312
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1313

    
1314
		response.setStatus(200);
1315
		response.setContentType("text/xml");
1316
		OutputStream out = response.getOutputStream();
1317

    
1318
		// write new identifier to the output stream
1319
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1320
	}
1321
    
1322
    /**
1323
     * Retrieve System Metadata
1324
     * @param pid
1325
     * @throws InvalidToken
1326
     * @throws ServiceFailure
1327
     * @throws NotAuthorized
1328
     * @throws NotFound
1329
     * @throws InvalidRequest
1330
     * @throws NotImplemented
1331
     * @throws IOException
1332
     * @throws JiBXException
1333
     */
1334
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1335

    
1336
        Identifier id = new Identifier();
1337
        id.setValue(pid);
1338
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1339
        
1340
        response.setContentType("text/xml");
1341
        response.setStatus(200);
1342
        OutputStream out = response.getOutputStream();
1343
        
1344
        // Serialize and write it to the output stream
1345
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1346
   }
1347
    
1348
    
1349
    /**
1350
     * Inserts or updates the object
1351
     * 
1352
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1353
     *               is the existing pid.  If insert, the pid is the new one
1354
     * @throws InvalidRequest 
1355
     * @throws ServiceFailure 
1356
     * @throws JiBXException 
1357
     * @throws NotImplemented 
1358
     * @throws InvalidSystemMetadata 
1359
     * @throws InsufficientResources 
1360
     * @throws UnsupportedType 
1361
     * @throws IdentifierNotUnique 
1362
     * @throws NotAuthorized 
1363
     * @throws InvalidToken 
1364
     * @throws NotFound 
1365
     * @throws IOException 
1366
     * @throws IllegalAccessException 
1367
     * @throws InstantiationException 
1368
     */
1369
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1370
       
1371
    	// Read the incoming data from its Mime Multipart encoding
1372
    	Map<String, File> files = collectMultipartFiles();
1373
               
1374
    	Identifier pid = new Identifier();
1375
        if (trailingPid == null) {
1376
	        // get the pid string from the body and set the value
1377
	        String pidString = multipartparams.get("pid").get(0);
1378
	        if (pidString != null) {
1379
            pid.setValue(pidString);
1380
            
1381
          } else {
1382
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1383
              
1384
          }
1385
        } else {
1386
        	// use the pid included in the URL
1387
        	pid.setValue(trailingPid);
1388
        }
1389
        logMetacat.debug("putObject with pid " + pid.getValue());
1390
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1391

    
1392
        InputStream object = null;
1393
        InputStream sysmeta = null;
1394
        File smFile = files.get("sysmeta");
1395
        sysmeta = new FileInputStream(smFile);
1396
        File objFile = files.get("object");
1397
        object = new FileInputStream(objFile);
1398
        
1399
        // ensure we have the object bytes
1400
        if  ( objFile == null ) {
1401
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1402
            
1403
        }
1404
        
1405
        // ensure we have the system metadata
1406
        if  ( smFile == null ) {
1407
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1408
            
1409
        }
1410
        
1411
        response.setStatus(200);
1412
        response.setContentType("text/xml");
1413
        OutputStream out = response.getOutputStream();
1414
        
1415
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1416
            // handle inserts
1417
            logMetacat.debug("Commence creation...");
1418
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1419

    
1420
            logMetacat.debug("creating object with pid " + pid.getValue());
1421
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1422
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1423
            
1424
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1425
        	// handle updates
1426
        	
1427
            // construct pids
1428
            Identifier newPid = null;
1429
            try {
1430
            	String newPidString = multipartparams.get("newPid").get(0);
1431
            	newPid = new Identifier();
1432
            	newPid.setValue(newPidString);
1433
            } catch (Exception e) {
1434
				logMetacat.error("Could not get newPid from request");
1435
			}
1436
            logMetacat.debug("Commence update...");
1437
            
1438
            // get the systemmetadata object
1439
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1440

    
1441
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1442
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1443
        } else {
1444
            throw new InvalidRequest("1000", "Operation must be create or update.");
1445
        }
1446
   
1447
    }
1448

    
1449
    /**
1450
     * Handle delete 
1451
     * @param pid ID of data object to be deleted
1452
     * @throws IOException
1453
     * @throws InvalidRequest 
1454
     * @throws NotImplemented 
1455
     * @throws NotFound 
1456
     * @throws NotAuthorized 
1457
     * @throws ServiceFailure 
1458
     * @throws InvalidToken 
1459
     * @throws JiBXException 
1460
     */
1461
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1462
    {
1463

    
1464
        OutputStream out = response.getOutputStream();
1465
        response.setStatus(200);
1466
        response.setContentType("text/xml");
1467

    
1468
        Identifier id = new Identifier();
1469
        id.setValue(pid);
1470

    
1471
        logMetacat.debug("Calling delete");
1472
        MNodeService.getInstance(request).delete(session, id);
1473
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1474
        
1475
    }
1476
    
1477
    /**
1478
     * Archives the given pid
1479
     * @param pid
1480
     * @throws InvalidToken
1481
     * @throws ServiceFailure
1482
     * @throws NotAuthorized
1483
     * @throws NotFound
1484
     * @throws NotImplemented
1485
     * @throws IOException
1486
     * @throws JiBXException
1487
     */
1488
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1489

    
1490
        OutputStream out = response.getOutputStream();
1491
        response.setStatus(200);
1492
        response.setContentType("text/xml");
1493

    
1494
        Identifier id = new Identifier();
1495
        id.setValue(pid);
1496

    
1497
        logMetacat.debug("Calling archive");
1498
        MNodeService.getInstance(request).archive(session, id);
1499
        
1500
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1501
        
1502
    }
1503

    
1504
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1505
		
1506
		// Read the incoming data from its Mime Multipart encoding
1507
		logMetacat.debug("Disassembling MIME multipart form");
1508
		InputStream sf = null;
1509
	
1510
		// handle MMP inputs
1511
		File tmpDir = getTempDirectory();
1512
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1513
		MultipartRequestResolver mrr = 
1514
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1515
		MultipartRequest mr = null;
1516
		try {
1517
			mr = mrr.resolveMultipart(request);
1518
		} catch (Exception e) {
1519
			throw new ServiceFailure("2161", 
1520
					"Could not resolve multipart: " + e.getMessage());
1521
		}
1522
		logMetacat.debug("resolved multipart request");
1523
		Map<String, File> files = mr.getMultipartFiles();
1524
		if (files == null || files.keySet() == null) {
1525
			throw new InvalidRequest("2163",
1526
					"must have multipart file with name 'message'");
1527
		}
1528
		logMetacat.debug("got multipart files");
1529
	
1530
		multipartparams = mr.getMultipartParameters();
1531
	
1532
		File sfFile = files.get("message");
1533
		if (sfFile == null) {
1534
			throw new InvalidRequest("2163",
1535
					"Missing the required file-part 'message' from the multipart request.");
1536
		}
1537
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1538
		sf = new FileInputStream(sfFile);
1539
	
1540
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1541
		return syncFailed;
1542
	}
1543

    
1544
}
(8-8/9)