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-08 18:10:07 -0700 (Mon, 08 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_PACKAGE = "package";
143
    protected static final String RESOURCE_VIEWS = "views";
144

    
145

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

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

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

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

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

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

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

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

    
390
	                String format = null;
391
	                String pid = null;
392

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

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

    
451
    private void doQuery(String engine, String query) {
452
    	
453
		OutputStream out = null;
454

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

    
473
	    			// set the content-type if we have it from the implementation
474
	    			if (stream instanceof ContentTypeInputStream) {
475
		    			//response.setContentType("application/octet-stream");
476
		    			//response.setContentType("text/xml");
477
	    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
478
	    			}
479
	                response.setStatus(200);
480
	                out = response.getOutputStream();
481
	                // write the results to the output stream
482
	                IOUtils.copyLarge(stream, out);
483
	                return;
484
	    		} else {
485
	    			MNodeService mnode = MNodeService.getInstance(request);
486
	    			mnode.setSession(session);
487
	    			QueryEngineDescription qed = mnode.getQueryEngineDescription(engine);
488
	    			response.setContentType("text/xml");
489
		            response.setStatus(200);
490
		            out = response.getOutputStream();
491
		            TypeMarshaller.marshalTypeToOutputStream(qed, out);
492
		            return;
493
	    		}
494
	    	}
495
	        
496
	        
497
    	} catch (BaseException be) {
498
        	// report Exceptions as clearly as possible
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
			try {
509
				out = response.getOutputStream();
510
			} catch (IOException ioe) {
511
				logMetacat.error("Could not get output stream from response", ioe);
512
			}
513
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
514
            serializeException(se, out);
515
        }
516
    }
517
    
518
    private void doViews(String format, String pid) {
519
    	
520
		OutputStream out = null;
521
		MNodeService mnode = MNodeService.getInstance(request);
522

    
523
    	try {
524
    		// get a list of views
525
    		if (pid != null) {
526
    			Identifier identifier = new Identifier();
527
    			identifier.setValue(pid);
528
				InputStream stream = mnode.getView(session, identifier, format);
529

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

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

    
674
        // call the service
675
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
676
        response.setStatus(200);
677
        response.setContentType("text/xml");
678
        OutputStream out = response.getOutputStream();
679
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
680
    }
681

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

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

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

    
844
    	Date date = null;
845
    	
846
    	try {
847
    		// try the expected date format
848
        // a date format for date string arguments
849
        DateFormat dateFormat = 
850
        	new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
851

    
852
	      date = dateFormat.parse(fromDateStr);
853
      
854
    	} catch (ParseException e) {
855
    		// try the date with the UTC indicator
856
        DateFormat utcDateFormat = 
857
        	new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
858
        utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
859
        date = utcDateFormat.parse(fromDateStr);
860
        
861
      }
862
    	
863
    	return date;
864
    }
865

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

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

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

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

    
1005
    }
1006

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

    
1044
        Identifier id = new Identifier();
1045
        id.setValue(pid);
1046

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

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

    
1181
            out = response.getOutputStream();  
1182
            IOUtils.copyLarge(data, out);
1183
            
1184
        }
1185
        else
1186
        { //call listObjects with specified params
1187
            Date startTime = null;
1188
            Date endTime = null;
1189
            ObjectFormatIdentifier formatId = null;
1190
            boolean replicaStatus = true;
1191
            int start = 0;
1192
            //TODO: make the max count into a const
1193
            int count = 1000;
1194
            Enumeration paramlist = request.getParameterNames();
1195
            while (paramlist.hasMoreElements()) 
1196
            { //parse the params and make the crud call
1197
                String name = (String) paramlist.nextElement();
1198
                String[] value = (String[])request.getParameterValues(name);
1199

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

    
1270
    /**
1271
     * Retrieve data package as Bagit zip
1272
     * @param pid
1273
     * @throws NotImplemented 
1274
     * @throws NotFound 
1275
     * @throws NotAuthorized 
1276
     * @throws ServiceFailure 
1277
     * @throws InvalidToken 
1278
     * @throws IOException 
1279
     */
1280
    protected void getPackage(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException {
1281

    
1282
        Identifier id = new Identifier();
1283
        id.setValue(pid);
1284
        InputStream is = MNodeService.getInstance(request).getPackage(session, id);
1285
        
1286
        response.setContentType("application/zip");
1287
        response.setStatus(200);
1288
        OutputStream out = response.getOutputStream();
1289
        
1290
        // write it to the output stream
1291
        IOUtils.copyLarge(is, out);
1292
   }
1293
    
1294
    /**
1295
     * Retrieve System Metadata
1296
     * @param pid
1297
     * @throws InvalidToken
1298
     * @throws ServiceFailure
1299
     * @throws NotAuthorized
1300
     * @throws NotFound
1301
     * @throws InvalidRequest
1302
     * @throws NotImplemented
1303
     * @throws IOException
1304
     * @throws JiBXException
1305
     */
1306
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1307

    
1308
        Identifier id = new Identifier();
1309
        id.setValue(pid);
1310
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1311
        
1312
        response.setContentType("text/xml");
1313
        response.setStatus(200);
1314
        OutputStream out = response.getOutputStream();
1315
        
1316
        // Serialize and write it to the output stream
1317
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1318
   }
1319
    
1320
    
1321
    /**
1322
     * Inserts or updates the object
1323
     * 
1324
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1325
     *               is the existing pid.  If insert, the pid is the new one
1326
     * @throws InvalidRequest 
1327
     * @throws ServiceFailure 
1328
     * @throws JiBXException 
1329
     * @throws NotImplemented 
1330
     * @throws InvalidSystemMetadata 
1331
     * @throws InsufficientResources 
1332
     * @throws UnsupportedType 
1333
     * @throws IdentifierNotUnique 
1334
     * @throws NotAuthorized 
1335
     * @throws InvalidToken 
1336
     * @throws NotFound 
1337
     * @throws IOException 
1338
     * @throws IllegalAccessException 
1339
     * @throws InstantiationException 
1340
     */
1341
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1342
       
1343
    	// Read the incoming data from its Mime Multipart encoding
1344
    	Map<String, File> files = collectMultipartFiles();
1345
               
1346
    	Identifier pid = new Identifier();
1347
        if (trailingPid == null) {
1348
	        // get the pid string from the body and set the value
1349
	        String pidString = multipartparams.get("pid").get(0);
1350
	        if (pidString != null) {
1351
            pid.setValue(pidString);
1352
            
1353
          } else {
1354
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1355
              
1356
          }
1357
        } else {
1358
        	// use the pid included in the URL
1359
        	pid.setValue(trailingPid);
1360
        }
1361
        logMetacat.debug("putObject with pid " + pid.getValue());
1362
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1363

    
1364
        InputStream object = null;
1365
        InputStream sysmeta = null;
1366
        File smFile = files.get("sysmeta");
1367
        sysmeta = new FileInputStream(smFile);
1368
        File objFile = files.get("object");
1369
        object = new FileInputStream(objFile);
1370
        
1371
        // ensure we have the object bytes
1372
        if  ( objFile == null ) {
1373
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1374
            
1375
        }
1376
        
1377
        // ensure we have the system metadata
1378
        if  ( smFile == null ) {
1379
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1380
            
1381
        }
1382
        
1383
        response.setStatus(200);
1384
        response.setContentType("text/xml");
1385
        OutputStream out = response.getOutputStream();
1386
        
1387
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1388
            // handle inserts
1389
            logMetacat.debug("Commence creation...");
1390
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1391

    
1392
            logMetacat.debug("creating object with pid " + pid.getValue());
1393
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1394
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1395
            
1396
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1397
        	// handle updates
1398
        	
1399
            // construct pids
1400
            Identifier newPid = null;
1401
            try {
1402
            	String newPidString = multipartparams.get("newPid").get(0);
1403
            	newPid = new Identifier();
1404
            	newPid.setValue(newPidString);
1405
            } catch (Exception e) {
1406
				logMetacat.error("Could not get newPid from request");
1407
			}
1408
            logMetacat.debug("Commence update...");
1409
            
1410
            // get the systemmetadata object
1411
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1412

    
1413
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1414
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1415
        } else {
1416
            throw new InvalidRequest("1000", "Operation must be create or update.");
1417
        }
1418
   
1419
    }
1420

    
1421
    /**
1422
     * Handle delete 
1423
     * @param pid ID of data object to be deleted
1424
     * @throws IOException
1425
     * @throws InvalidRequest 
1426
     * @throws NotImplemented 
1427
     * @throws NotFound 
1428
     * @throws NotAuthorized 
1429
     * @throws ServiceFailure 
1430
     * @throws InvalidToken 
1431
     * @throws JiBXException 
1432
     */
1433
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1434
    {
1435

    
1436
        OutputStream out = response.getOutputStream();
1437
        response.setStatus(200);
1438
        response.setContentType("text/xml");
1439

    
1440
        Identifier id = new Identifier();
1441
        id.setValue(pid);
1442

    
1443
        logMetacat.debug("Calling delete");
1444
        MNodeService.getInstance(request).delete(session, id);
1445
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1446
        
1447
    }
1448
    
1449
    /**
1450
     * Archives the given pid
1451
     * @param pid
1452
     * @throws InvalidToken
1453
     * @throws ServiceFailure
1454
     * @throws NotAuthorized
1455
     * @throws NotFound
1456
     * @throws NotImplemented
1457
     * @throws IOException
1458
     * @throws JiBXException
1459
     */
1460
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1461

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

    
1466
        Identifier id = new Identifier();
1467
        id.setValue(pid);
1468

    
1469
        logMetacat.debug("Calling archive");
1470
        MNodeService.getInstance(request).archive(session, id);
1471
        
1472
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1473
        
1474
    }
1475

    
1476
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1477
		
1478
		// Read the incoming data from its Mime Multipart encoding
1479
		logMetacat.debug("Disassembling MIME multipart form");
1480
		InputStream sf = null;
1481
	
1482
		// handle MMP inputs
1483
		File tmpDir = getTempDirectory();
1484
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1485
		MultipartRequestResolver mrr = 
1486
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1487
		MultipartRequest mr = null;
1488
		try {
1489
			mr = mrr.resolveMultipart(request);
1490
		} catch (Exception e) {
1491
			throw new ServiceFailure("2161", 
1492
					"Could not resolve multipart: " + e.getMessage());
1493
		}
1494
		logMetacat.debug("resolved multipart request");
1495
		Map<String, File> files = mr.getMultipartFiles();
1496
		if (files == null || files.keySet() == null) {
1497
			throw new InvalidRequest("2163",
1498
					"must have multipart file with name 'message'");
1499
		}
1500
		logMetacat.debug("got multipart files");
1501
	
1502
		multipartparams = mr.getMultipartParameters();
1503
	
1504
		File sfFile = files.get("message");
1505
		if (sfFile == null) {
1506
			throw new InvalidRequest("2163",
1507
					"Missing the required file-part 'message' from the multipart request.");
1508
		}
1509
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1510
		sf = new FileInputStream(sfFile);
1511
	
1512
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1513
		return syncFailed;
1514
	}
1515

    
1516
}
(8-8/9)