Project

General

Profile

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

    
25
import java.io.File;
26
import java.io.FileInputStream;
27
import java.io.IOException;
28
import java.io.InputStream;
29
import java.io.OutputStream;
30
import java.util.Date;
31
import java.util.Enumeration;
32
import java.util.Map;
33
import java.util.concurrent.ExecutorService;
34
import java.util.concurrent.Executors;
35

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

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

    
79
import edu.ucsb.nceas.metacat.MetaCatServlet;
80
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeInputStream;
81
import edu.ucsb.nceas.metacat.dataone.MNodeService;
82
import edu.ucsb.nceas.metacat.properties.PropertyService;
83
import edu.ucsb.nceas.metacat.restservice.D1ResourceHandler;
84
import edu.ucsb.nceas.metacat.util.DeleteOnCloseFileInputStream;
85
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
86

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

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

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

    
142

    
143

    
144
    
145
    // shared executor
146
	private static ExecutorService executor = null;
147

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

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

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

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

    
344
	                String engine = null;
345
	                String query = null;
346

    
347
	                if (extra != null) {
348
		                // get the engine
349
		                int engineIndex = extra.length();
350
		                if (extra.indexOf("/") > -1) {
351
		                	engineIndex = extra.indexOf("/");
352
		                }
353
		                engine = extra.substring(0, engineIndex);
354
		                logMetacat.debug("query engine: " + engine);
355
		                
356
		                // get the query if it is there
357
		                query = extra.substring(engineIndex, extra.length());
358
		                if (query != null && query.length() == 0) {
359
		                	query = null;
360
		                } else {
361
		                	if (query.startsWith("/")) {
362
		                		query = query.substring(1);
363
		                    }
364
		                	// remove the query delimiter if it exists
365
		                	if (query.startsWith("?")) {
366
		                		query = query.substring(1);
367
		                    }
368
		                }
369
		                logMetacat.debug("query: " + query);
370

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

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

    
420
	                }
421
                    
422
                    // get
423
                    if (httpVerb == GET) {
424
                        
425
                        getPackage(format, pid);
426
                        status = true;
427
                    }  
428
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
429
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
430
	                // after the command
431
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
432
	                logMetacat.debug("view extra: " + extra);
433

    
434
	                String format = null;
435
	                String pid = null;
436

    
437
	                if (extra != null) {
438
		                // get the format
439
		                int formatIndex = extra.length();
440
		                if (extra.indexOf("/") > -1) {
441
		                	formatIndex = extra.indexOf("/");
442
		                }
443
		                format = extra.substring(0, formatIndex);
444
		                logMetacat.debug("view format: " + format);
445
		                
446
		                // get the pid if it is there
447
		                pid = extra.substring(formatIndex, extra.length());
448
		                if (pid != null && pid.length() == 0) {
449
		                	pid = null;
450
		                } else {
451
		                	if (pid.startsWith("/")) {
452
		                		pid = pid.substring(1);
453
		                    }
454
		                }
455
		                logMetacat.debug("pid: " + pid);
456

    
457
	                }
458
	                logMetacat.debug("verb:" + httpVerb);
459
	                if (httpVerb == GET) {
460
	                    doViews(format, pid);
461
	                    status = true;
462
	                }
463
                } 
464
                
465
                if (!status) {
466
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
467
                }
468
            } else {
469
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
470
            }
471
        } catch (BaseException be) {
472
        	// report Exceptions as clearly as possible
473
        	OutputStream out = null;
474
			try {
475
				out = response.getOutputStream();
476
			} catch (IOException e) {
477
				logMetacat.error("Could not get output stream from response", e);
478
			}
479
            serializeException(be, out);
480
        } catch (Exception e) {
481
            // report Exceptions as clearly and generically as possible
482
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
483
        	OutputStream out = null;
484
			try {
485
				out = response.getOutputStream();
486
			} catch (IOException ioe) {
487
				logMetacat.error("Could not get output stream from response", ioe);
488
			}
489
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
490
            serializeException(se, out);
491
        }
492
    }
493
    
494

    
495
    private void doQuery(String engine, String query) {
496
    	
497
		OutputStream out = null;
498

    
499
    	try {
500
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter			
501
	    	if (engine == null) {
502
	    		// just looking for list of engines
503
	    		MNodeService mnode = MNodeService.getInstance(request);
504
    			mnode.setSession(session);
505
	    		QueryEngineList qel = mnode.listQueryEngines(session);
506
	    		response.setContentType("text/xml");
507
	            response.setStatus(200);
508
	            out = response.getOutputStream();
509
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
510
	            return;
511
	    	} else {
512
	    		if (query != null) {
513
	    			MNodeService mnode = MNodeService.getInstance(request);
514
	    			mnode.setSession(session);
515
	    			InputStream stream = mnode.query(session, engine, query);
516

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

    
567
    	try {
568
    		// get a list of views
569
    		if (pid != null) {
570
    			Identifier identifier = new Identifier();
571
    			identifier.setValue(pid);
572
				InputStream stream = mnode.view(session, format, identifier);
573

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

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

    
718
        // call the service
719
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
720
        response.setStatus(200);
721
        response.setContentType("text/xml");
722
        OutputStream out = response.getOutputStream();
723
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
724
    }
725

    
726
    /**
727
     * Checks the access policy
728
     * @param id
729
     * @return
730
     * @throws ServiceFailure
731
     * @throws InvalidToken
732
     * @throws NotFound
733
     * @throws NotAuthorized
734
     * @throws NotImplemented
735
     * @throws InvalidRequest
736
     */
737
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
738
		Identifier pid = new Identifier();
739
		pid.setValue(id);
740
		Permission permission = null;
741
		try {
742
			String perm = params.get("action")[0];
743
			permission = Permission.convert(perm);
744
		} catch (Exception e) {
745
			logMetacat.warn("No permission specified");
746
		}
747
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
748
		response.setStatus(200);
749
		response.setContentType("text/xml");
750
		return result;
751
    }
752
    
753
    private void getToken() throws Exception {
754
		
755
		if (this.session != null) {
756
			String userId = this.session.getSubject().getValue();
757

    
758
			String token = null;
759
			token = TokenGenerator.getJWT(userId);
760
			
761
			response.setStatus(200);
762
			response.setContentType("text/plain");
763
	        OutputStream out = response.getOutputStream();
764
	        out.write(token.getBytes(MetaCatServlet.DEFAULT_ENCODING));
765
	        out.close();
766
		} else {
767
			response.setStatus(401);
768
			response.setContentType("text/plain");
769
			OutputStream out = response.getOutputStream();
770
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
771
	        out.close();
772
		}
773
		
774
    }
775
    
776
    /**
777
     * Processes failed synchronization message
778
     * @throws NotImplemented
779
     * @throws ServiceFailure
780
     * @throws NotAuthorized
781
     * @throws InvalidRequest
782
     * @throws JiBXException
783
     * @throws IllegalAccessException 
784
     * @throws InstantiationException 
785
     * @throws IOException 
786
     */
787
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
788
    	SynchronizationFailed syncFailed = null;
789
		try {
790
			syncFailed = collectSynchronizationFailed();
791
		} catch (ParserConfigurationException e) {
792
			throw new ServiceFailure("2161", e.getMessage());
793
		} catch (SAXException e) {
794
			throw new ServiceFailure("2161", e.getMessage());
795
		}
796
		
797
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
798
    }
799

    
800
	/**
801
     * Calculate the checksum 
802
     * @throws NotImplemented
803
     * @throws JiBXException
804
     * @throws IOException
805
     * @throws InvalidToken
806
     * @throws ServiceFailure
807
     * @throws NotAuthorized
808
     * @throws NotFound
809
     * @throws InvalidRequest
810
     */
811
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
812
    	String checksumAlgorithm = "MD5";
813
    	try {
814
    		checksumAlgorithm = PropertyService.getProperty("dataone.checksumAlgorithm.default");
815
        } catch(Exception e) {
816
        	logMetacat.warn("Could not lookup configured default checksum algorithm, using: " + checksumAlgorithm);
817
        }    
818
        
819
        Identifier pidid = new Identifier();
820
        pidid.setValue(pid);
821
        try {
822
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
823
        } catch(Exception e) {
824
            //do nothing.  use the default
825
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
826
        }
827
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
828
        
829
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
830
        logMetacat.debug("got checksum " + c.getValue());
831
        response.setStatus(200);
832
        logMetacat.debug("serializing response");
833
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
834
        logMetacat.debug("done serializing response.");
835
        
836
    }
837
    
838
	/**
839
     * handle the replicate action for MN
840
	 * @throws JiBXException 
841
	 * @throws FileUploadException 
842
	 * @throws IOException 
843
	 * @throws InvalidRequest 
844
	 * @throws ServiceFailure 
845
	 * @throws UnsupportedType 
846
	 * @throws InsufficientResources 
847
	 * @throws NotAuthorized 
848
	 * @throws NotImplemented 
849
	 * @throws IllegalAccessException 
850
	 * @throws InstantiationException 
851
	 * @throws InvalidToken 
852
     */
853
    private void replicate() 
854
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException, 
855
        JiBXException, NotImplemented, NotAuthorized, InsufficientResources, 
856
        UnsupportedType, InstantiationException, IllegalAccessException, InvalidToken {
857

    
858
        logMetacat.debug("in POST replicate()");
859
        
860
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
861
        boolean allowed = false;
862
        if (session == null) {
863
        	String msg = "No session was provided.";
864
            NotAuthorized failure = new NotAuthorized("2152", msg);
865
        	throw failure;
866
        } else {
867
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
868
        	if (!allowed) {
869
        		String msg = "User is not an admin user";
870
                NotAuthorized failure = new NotAuthorized("2152", msg);
871
            	throw failure;
872
        	}
873
        }
874
        
875
        // parse the systemMetadata
876
        Map<String, File> files = collectMultipartFiles();        
877
        final SystemMetadata sysmeta = TypeMarshaller.unmarshalTypeFromFile(SystemMetadata.class, files.get("sysmeta"));
878
        
879
        String sn = multipartparams.get("sourceNode").get(0);
880
        logMetacat.debug("sourceNode: " + sn);
881
        final NodeReference sourceNode = new NodeReference();
882
        sourceNode.setValue(sn);
883
        
884
        // run it in a thread to avoid connection timeout
885
        Runnable runner = new Runnable() {
886
			@Override
887
			public void run() {
888
				try {
889
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
890
				} catch (Exception e) {
891
					logMetacat.error("Error running replication: " + e.getMessage(), e);
892
					throw new RuntimeException(e.getMessage(), e);
893
				}
894
			}
895
    	};
896
    	// submit the task, and that's it
897
    	executor.submit(runner);
898
        
899
    	// thread was started, so we return success
900
        response.setStatus(200);
901
        
902
    }
903

    
904
    /**
905
     * Handle the getReplica action for the MN
906
     * @param id  the identifier for the object
907
     * @throws NotFound 
908
     * @throws ServiceFailure 
909
     * @throws NotImplemented 
910
     * @throws NotAuthorized 
911
     * @throws InvalidToken 
912
     * @throws InvalidRequest 
913
     */
914
    private void getReplica(String id) 
915
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
916
        ServiceFailure, NotFound {
917
        
918
        Identifier pid = new Identifier();
919
        pid.setValue(id);
920
        OutputStream out = null;
921
        InputStream dataBytes = null;
922
                
923
        try {
924
            // call the service
925
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
926

    
927
            response.setContentType("application/octet-stream");
928
            response.setStatus(200);
929
            out = response.getOutputStream();
930
            // write the object to the output stream
931
            IOUtils.copyLarge(dataBytes, out);
932
            
933
        } catch (IOException e) {
934
            String msg = "There was an error writing the output: " + e.getMessage();
935
            logMetacat.error(msg);
936
            throw new ServiceFailure("2181", msg);
937
        
938
        }
939

    
940
    }
941

    
942
    /**
943
     * Get the Node information
944
     * 
945
     * @throws JiBXException
946
     * @throws IOException
947
     * @throws InvalidRequest 
948
     * @throws ServiceFailure 
949
     * @throws NotAuthorized 
950
     * @throws NotImplemented 
951
     */
952
    private void node() 
953
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
954
        
955
        Node n = MNodeService.getInstance(request).getCapabilities();
956
        
957
        response.setContentType("text/xml");
958
        response.setStatus(200);
959
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
960
        
961
    }
962
    
963
    /**
964
     * MN_crud.describe()
965
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
966
     * @param pid
967
     * @throws InvalidRequest 
968
     * @throws NotImplemented 
969
     * @throws NotFound 
970
     * @throws NotAuthorized 
971
     * @throws ServiceFailure 
972
     * @throws InvalidToken 
973
     */
974
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
975
    {
976
        
977
        response.setContentType("text/xml");
978

    
979
        Identifier id = new Identifier();
980
        id.setValue(pid);
981

    
982
        DescribeResponse dr = null;
983
        try {
984
        	dr = MNodeService.getInstance(request).describe(session, id);
985
        } catch (BaseException e) {
986
        	response.setStatus(e.getCode());
987
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
988
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
989
            response.addHeader("DataONE-Exception-Description", e.getDescription());
990
            response.addHeader("DataONE-Exception-PID", id.getValue());
991
            return;
992
		}
993
        
994
        response.setStatus(200);
995
        
996
        //response.addHeader("pid", pid);
997
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
998
        response.addHeader("Content-Length", dr.getContent_Length() + "");
999
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
1000
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
1001
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
1002

    
1003
        
1004
    }
1005
    
1006
    /**
1007
     * get the logs based on passed params.  Available 
1008
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
1009
     * for more info
1010
     * @throws NotImplemented 
1011
     * @throws InvalidRequest 
1012
     * @throws NotAuthorized 
1013
     * @throws ServiceFailure 
1014
     * @throws InvalidToken 
1015
     * @throws IOException 
1016
     * @throws JiBXException 
1017
     */
1018
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
1019
    {
1020
            
1021
        Date fromDate = null;
1022
        Date toDate = null;
1023
        String event = null;
1024
        Integer start = null;
1025
        Integer count = null;
1026
        String pidFilter = null;
1027
        
1028
        try {
1029
        	String fromDateS = params.get("fromDate")[0];
1030
            logMetacat.debug("param fromDateS: " + fromDateS);
1031
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1032
        } catch (Exception e) {
1033
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1034
        }
1035
        try {
1036
        	String toDateS = params.get("toDate")[0];
1037
            logMetacat.debug("param toDateS: " + toDateS);
1038
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1039
        } catch (Exception e) {
1040
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1041
		}
1042
        try {
1043
        	event = params.get("event")[0];
1044
        } catch (Exception e) {
1045
        	logMetacat.warn("Could not parse event: " + e.getMessage());
1046
		}
1047
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
1048
        
1049
        try {
1050
        	start =  Integer.parseInt(params.get("start")[0]);
1051
        } catch (Exception e) {
1052
			logMetacat.warn("Could not parse start: " + e.getMessage());
1053
		}
1054
        try {
1055
        	count =  Integer.parseInt(params.get("count")[0]);
1056
        } catch (Exception e) {
1057
			logMetacat.warn("Could not parse count: " + e.getMessage());
1058
		}
1059
        
1060
        try {
1061
            pidFilter = params.get("pidFilter")[0];
1062
        } catch (Exception e) {
1063
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1064
        }
1065
        
1066
        logMetacat.debug("calling getLogRecords");
1067
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1068
        
1069
        OutputStream out = response.getOutputStream();
1070
        response.setStatus(200);
1071
        response.setContentType("text/xml");
1072
        
1073
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1074
        
1075
    }
1076
    
1077
    
1078
    
1079
    /**
1080
     * Implements REST version of DataONE CRUD API --> get
1081
     * @param pid ID of data object to be read
1082
     * @throws NotImplemented 
1083
     * @throws InvalidRequest 
1084
     * @throws NotFound 
1085
     * @throws NotAuthorized 
1086
     * @throws ServiceFailure 
1087
     * @throws InvalidToken 
1088
     * @throws IOException 
1089
     * @throws JiBXException 
1090
     */
1091
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1092
        OutputStream out = null;
1093
        
1094
        if (pid != null) { //get a specific document                
1095
            Identifier id = new Identifier();
1096
            id.setValue(pid);
1097
                
1098
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1099
            
1100
            // set the headers for the content
1101
            String mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1102
            if (mimeType == null) {
1103
            	mimeType = "application/octet-stream";
1104
            }
1105
            String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1106
            String filename = id.getValue();
1107
            if (extension != null) {
1108
            	filename = id.getValue() + extension;
1109
            }
1110
            response.setContentType(mimeType);
1111
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1112
            
1113
            InputStream data = MNodeService.getInstance(request).get(session, id);
1114

    
1115
            out = response.getOutputStream();  
1116
            IOUtils.copyLarge(data, out);
1117
            
1118
        }
1119
        else
1120
        { //call listObjects with specified params
1121
            Date startTime = null;
1122
            Date endTime = null;
1123
            ObjectFormatIdentifier formatId = null;
1124
            Identifier identifier = null;
1125
            boolean replicaStatus = true;
1126
            int start = 0;
1127
            //TODO: make the max count into a const
1128
            int count = 1000;
1129
            Enumeration paramlist = request.getParameterNames();
1130
            while (paramlist.hasMoreElements()) 
1131
            { //parse the params and make the crud call
1132
                String name = (String) paramlist.nextElement();
1133
                String[] value = (String[])request.getParameterValues(name);
1134

    
1135
                if (name.equals("fromDate") && value != null)
1136
                {
1137
                    try
1138
                    {
1139
                      //startTime = dateFormat.parse(value[0]);
1140
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1141
                        //startTime = parseDateAndConvertToGMT(value[0]);
1142
                    }
1143
                    catch(Exception e)
1144
                    {  //if we can't parse it, just don't use the fromDate param
1145
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1146
                        startTime = null;
1147
                    }
1148
                }
1149
                else if(name.equals("toDate") && value != null)
1150
                {
1151
                    try
1152
                    {
1153
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1154
                    }
1155
                    catch(Exception e)
1156
                    {  //if we can't parse it, just don't use the toDate param
1157
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1158
                        endTime = null;
1159
                    }
1160
                }
1161
                else if(name.equals("formatId") && value != null) 
1162
                {
1163
                	formatId = new ObjectFormatIdentifier();
1164
                	formatId.setValue(value[0]);
1165
                }
1166
                else if(name.equals("identifier") && value != null) 
1167
                {
1168
                	identifier = new Identifier();
1169
                	identifier.setValue(value[0]);
1170
                }
1171
                else if(name.equals("replicaStatus") && value != null)
1172
                {
1173
                    if(value != null && 
1174
                       value.length > 0 && 
1175
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1176
                    {
1177
                        replicaStatus = false;
1178
                    }
1179
                }
1180
                else if(name.equals("start") && value != null)
1181
                {
1182
                    start = new Integer(value[0]).intValue();
1183
                }
1184
                else if(name.equals("count") && value != null)
1185
                {
1186
                    count = new Integer(value[0]).intValue();
1187
                }
1188
            }
1189
            //make the crud call
1190
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1191
                    " endTime: " + endTime + " formatId: " + 
1192
                    formatId + " replicaStatus: " + replicaStatus + 
1193
                    " start: " + start + " count: " + count);
1194
           
1195
            ObjectList ol = 
1196
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1197
           			formatId, identifier, replicaStatus, start, count);
1198
           
1199
            out = response.getOutputStream();  
1200
            response.setStatus(200);
1201
            response.setContentType("text/xml");
1202
            // Serialize and write it to the output stream
1203
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1204
            
1205
        }
1206
        
1207
    }
1208
    
1209

    
1210
    /**
1211
     * Retrieve data package as Bagit zip
1212
     * @param pid
1213
     * @throws NotImplemented 
1214
     * @throws NotFound 
1215
     * @throws NotAuthorized 
1216
     * @throws ServiceFailure 
1217
     * @throws InvalidToken 
1218
     * @throws IOException 
1219
     * @throws InvalidRequest 
1220
     */
1221
    protected void getPackage(String format, String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1222

    
1223
        Identifier id = new Identifier();
1224
        id.setValue(pid);
1225
        ObjectFormatIdentifier formatId = null;
1226
        if (format != null) {
1227
        	formatId = new ObjectFormatIdentifier();
1228
        	formatId.setValue(format);
1229
        }
1230
		InputStream is = MNodeService.getInstance(request).getPackage(session, formatId , id);
1231
        
1232
        // use the provided filename
1233
        String filename = null;
1234
        if (is instanceof DeleteOnCloseFileInputStream) {
1235
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1236
        } else {
1237
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1238
        }
1239
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1240
        response.setContentType("application/zip");
1241
        response.setStatus(200);
1242
        OutputStream out = response.getOutputStream();
1243
        
1244
        // write it to the output stream
1245
        IOUtils.copyLarge(is, out);
1246
   }
1247
    
1248
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1249
			NotAuthorized, NotFound, NotImplemented, IOException,
1250
			JiBXException, InvalidRequest, IdentifierNotUnique,
1251
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1252

    
1253
		// publish the object
1254
		Identifier originalIdentifier = new Identifier();
1255
		originalIdentifier.setValue(pid);
1256
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1257

    
1258
		response.setStatus(200);
1259
		response.setContentType("text/xml");
1260
		OutputStream out = response.getOutputStream();
1261

    
1262
		// write new identifier to the output stream
1263
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1264
	}
1265
    
1266
    /**
1267
     * Retrieve System Metadata
1268
     * @param pid
1269
     * @throws InvalidToken
1270
     * @throws ServiceFailure
1271
     * @throws NotAuthorized
1272
     * @throws NotFound
1273
     * @throws InvalidRequest
1274
     * @throws NotImplemented
1275
     * @throws IOException
1276
     * @throws JiBXException
1277
     */
1278
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1279

    
1280
        Identifier id = new Identifier();
1281
        id.setValue(pid);
1282
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1283
        
1284
        response.setContentType("text/xml");
1285
        response.setStatus(200);
1286
        OutputStream out = response.getOutputStream();
1287
        
1288
        // Serialize and write it to the output stream
1289
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1290
   }
1291
    
1292
    
1293
    /**
1294
     * Inserts or updates the object
1295
     * 
1296
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1297
     *               is the existing pid.  If insert, the pid is the new one
1298
     * @throws InvalidRequest 
1299
     * @throws ServiceFailure 
1300
     * @throws JiBXException 
1301
     * @throws NotImplemented 
1302
     * @throws InvalidSystemMetadata 
1303
     * @throws InsufficientResources 
1304
     * @throws UnsupportedType 
1305
     * @throws IdentifierNotUnique 
1306
     * @throws NotAuthorized 
1307
     * @throws InvalidToken 
1308
     * @throws NotFound 
1309
     * @throws IOException 
1310
     * @throws IllegalAccessException 
1311
     * @throws InstantiationException 
1312
     */
1313
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1314
       
1315
    	// Read the incoming data from its Mime Multipart encoding
1316
    	Map<String, File> files = collectMultipartFiles();
1317
               
1318
    	Identifier pid = new Identifier();
1319
        if (trailingPid == null) {
1320
	        // get the pid string from the body and set the value
1321
	        String pidString = multipartparams.get("pid").get(0);
1322
	        if (pidString != null) {
1323
            pid.setValue(pidString);
1324
            
1325
          } else {
1326
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1327
              
1328
          }
1329
        } else {
1330
        	// use the pid included in the URL
1331
        	pid.setValue(trailingPid);
1332
        }
1333
        logMetacat.debug("putObject with pid " + pid.getValue());
1334
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1335

    
1336
        InputStream object = null;
1337
        InputStream sysmeta = null;
1338
        File smFile = files.get("sysmeta");
1339
        sysmeta = new FileInputStream(smFile);
1340
        File objFile = files.get("object");
1341
        object = new FileInputStream(objFile);
1342
        
1343
        // ensure we have the object bytes
1344
        if  ( objFile == null ) {
1345
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1346
            
1347
        }
1348
        
1349
        // ensure we have the system metadata
1350
        if  ( smFile == null ) {
1351
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1352
            
1353
        }
1354
        
1355
        response.setStatus(200);
1356
        response.setContentType("text/xml");
1357
        OutputStream out = response.getOutputStream();
1358
        
1359
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1360
            // handle inserts
1361
            logMetacat.debug("Commence creation...");
1362
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1363

    
1364
            logMetacat.debug("creating object with pid " + pid.getValue());
1365
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1366
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1367
            
1368
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1369
        	// handle updates
1370
        	
1371
            // construct pids
1372
            Identifier newPid = null;
1373
            try {
1374
            	String newPidString = multipartparams.get("newPid").get(0);
1375
            	newPid = new Identifier();
1376
            	newPid.setValue(newPidString);
1377
            } catch (Exception e) {
1378
				logMetacat.error("Could not get newPid from request");
1379
			}
1380
            logMetacat.debug("Commence update...");
1381
            
1382
            // get the systemmetadata object
1383
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1384

    
1385
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1386
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1387
        } else {
1388
            throw new InvalidRequest("1000", "Operation must be create or update.");
1389
        }
1390
   
1391
    }
1392

    
1393
    /**
1394
     * Handle delete 
1395
     * @param pid ID of data object to be deleted
1396
     * @throws IOException
1397
     * @throws InvalidRequest 
1398
     * @throws NotImplemented 
1399
     * @throws NotFound 
1400
     * @throws NotAuthorized 
1401
     * @throws ServiceFailure 
1402
     * @throws InvalidToken 
1403
     * @throws JiBXException 
1404
     */
1405
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1406
    {
1407

    
1408
        OutputStream out = response.getOutputStream();
1409
        response.setStatus(200);
1410
        response.setContentType("text/xml");
1411

    
1412
        Identifier id = new Identifier();
1413
        id.setValue(pid);
1414

    
1415
        logMetacat.debug("Calling delete");
1416
        MNodeService.getInstance(request).delete(session, id);
1417
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1418
        
1419
    }
1420
    
1421
    /**
1422
     * Archives the given pid
1423
     * @param pid
1424
     * @throws InvalidToken
1425
     * @throws ServiceFailure
1426
     * @throws NotAuthorized
1427
     * @throws NotFound
1428
     * @throws NotImplemented
1429
     * @throws IOException
1430
     * @throws JiBXException
1431
     */
1432
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1433

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

    
1438
        Identifier id = new Identifier();
1439
        id.setValue(pid);
1440

    
1441
        logMetacat.debug("Calling archive");
1442
        MNodeService.getInstance(request).archive(session, id);
1443
        
1444
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1445
        
1446
    }
1447

    
1448
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1449
		
1450
		// Read the incoming data from its Mime Multipart encoding
1451
		logMetacat.debug("Disassembling MIME multipart form");
1452
		InputStream sf = null;
1453
	
1454
		// handle MMP inputs
1455
		File tmpDir = getTempDirectory();
1456
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1457
		MultipartRequestResolver mrr = 
1458
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1459
		MultipartRequest mr = null;
1460
		try {
1461
			mr = mrr.resolveMultipart(request);
1462
		} catch (Exception e) {
1463
			throw new ServiceFailure("2161", 
1464
					"Could not resolve multipart: " + e.getMessage());
1465
		}
1466
		logMetacat.debug("resolved multipart request");
1467
		Map<String, File> files = mr.getMultipartFiles();
1468
		if (files == null || files.keySet() == null) {
1469
			throw new InvalidRequest("2163",
1470
					"must have multipart file with name 'message'");
1471
		}
1472
		logMetacat.debug("got multipart files");
1473
	
1474
		multipartparams = mr.getMultipartParameters();
1475
	
1476
		File sfFile = files.get("message");
1477
		if (sfFile == null) {
1478
			throw new InvalidRequest("2163",
1479
					"Missing the required file-part 'message' from the multipart request.");
1480
		}
1481
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1482
		sf = new FileInputStream(sfFile);
1483
	
1484
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1485
		return syncFailed;
1486
	}
1487

    
1488
}
(3-3/4)