Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: leinfelder $'
7
 *     '$Date: 2015-09-24 13:59:54 -0700 (Thu, 24 Sep 2015) $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.restservice.v1;
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.Event;
63
import org.dataone.service.types.v1.Identifier;
64
import org.dataone.service.types.v1.Log;
65
import org.dataone.service.types.v1.Node;
66
import org.dataone.service.types.v1.NodeReference;
67
import org.dataone.service.types.v1.ObjectFormatIdentifier;
68
import org.dataone.service.types.v1.ObjectList;
69
import org.dataone.service.types.v1.Permission;
70
import org.dataone.service.types.v1.Person;
71
import org.dataone.service.types.v1.SystemMetadata;
72
import org.dataone.service.types.v1_1.QueryEngineDescription;
73
import org.dataone.service.types.v1_1.QueryEngineList;
74
import org.dataone.service.util.Constants;
75
import org.dataone.service.util.DateTimeMarshaller;
76
import org.dataone.service.util.ExceptionHandler;
77
import org.dataone.service.util.TypeMarshaller;
78
import org.jibx.runtime.JiBXException;
79
import org.xml.sax.SAXException;
80

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

    
89

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

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

    
132
    // MN-specific API Resources
133
    protected static final String RESOURCE_MONITOR = "monitor";
134
    protected static final String RESOURCE_REPLICATE = "replicate";
135
    protected static final String RESOURCE_REPLICAS = "replica";
136
    protected static final String RESOURCE_NODE = "node";
137
    protected static final String RESOURCE_ERROR = "error";
138
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
139
    protected static final String RESOURCE_GENERATE_ID = "generate";
140
    protected static final String RESOURCE_PUBLISH = "publish";
141
    protected static final String RESOURCE_PACKAGE = "packages";
142
    protected static final String RESOURCE_VIEWS = "views";
143
    protected static final String RESOURCE_TOKEN = "token";
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_TOKEN)) {
223
                    logMetacat.debug("Using resource 'token'");
224
                    // get
225
                    if (httpVerb == GET) {
226
                    	// after the command
227
                        getToken();
228
                        status = true;
229
                    }
230
                    
231
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
232
                    if (httpVerb == GET) {
233
                    	// after the command
234
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
235
                        extra = decode(extra);
236
	                	// check the access rules
237
	                    isAuthorized(extra);
238
	                    status = true;
239
	                    logMetacat.debug("done getting access");
240
                    }
241
                } else if (resource.startsWith(RESOURCE_META)) {
242
                    logMetacat.debug("Using resource 'meta'");
243
                    // get
244
                    if (httpVerb == GET) {
245
                    	// after the command
246
                        extra = parseTrailing(resource, RESOURCE_META);
247
                        extra = decode(extra);
248
                        getSystemMetadataObject(extra);
249
                        status = true;
250
                    }
251
                    
252
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
253
                    logMetacat.debug("Using resource 'object'");
254
                    // after the command
255
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
256
                    logMetacat.debug("objectId(before decoded: " + extra);
257
                    extra = decode(extra);
258
                    logMetacat.debug("objectId: " + extra);
259
                    logMetacat.debug("verb:" + httpVerb);
260

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

    
353
	                String engine = null;
354
	                String query = null;
355

    
356
	                if (extra != null) {
357
		                // get the engine
358
		                int engineIndex = extra.length();
359
		                if (extra.indexOf("/") > -1) {
360
		                	engineIndex = extra.indexOf("/");
361
		                }
362
		                engine = extra.substring(0, engineIndex);
363
		                engine = decode(engine);
364
		                logMetacat.debug("query engine: " + engine);
365
		                
366
		                // check the query string first
367
		                query = request.getQueryString();
368
		                
369
		                // if null, look at the whole endpoint
370
		                if (query == null) {
371
			                // get the query if it is there
372
			                query = extra.substring(engineIndex, extra.length());
373
			                if (query != null && query.length() == 0) {
374
			                	query = null;
375
			                } else {
376
			                	if (query.startsWith("/")) {
377
			                		query = query.substring(1);
378
			                    }
379
			                }
380
		                }
381
		                query = decode(query);
382
		                logMetacat.debug("query: " + query);
383

    
384
	                }
385
	                logMetacat.debug("verb:" + httpVerb);
386
	                if (httpVerb == GET) {
387
	                    doQuery(engine, query);
388
	                    status = true;
389
	                }
390
                } else if (resource.startsWith(RESOURCE_GENERATE_ID)) {
391
                	// generate an id
392
                    if (httpVerb == POST) {
393
                        generateIdentifier();
394
                        status = true;
395
                    }
396
                } else if (resource.startsWith(RESOURCE_PUBLISH)) {
397
                    logMetacat.debug("Using resource: " + RESOURCE_PUBLISH);
398
                    // PUT
399
                    if (httpVerb == PUT) {
400
                    	// after the command
401
                        extra = parseTrailing(resource, RESOURCE_PUBLISH);
402
                        extra = decode(extra);
403
                        publish(extra);
404
                        status = true;
405
                    }  
406
                } else if (resource.startsWith(RESOURCE_PACKAGE)) {
407
                    logMetacat.debug("Using resource: " + RESOURCE_PACKAGE);
408
                    // get
409
                    if (httpVerb == GET) {
410
                    	// after the command
411
                        extra = parseTrailing(resource, RESOURCE_PACKAGE);
412
                        extra = decode(extra);
413
                        getPackage(extra);
414
                        status = true;
415
                    }  
416
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
417
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
418
	                // after the command
419
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
420
	                logMetacat.debug("view extra: " + extra);
421

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

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

    
447
	                }
448
	                logMetacat.debug("verb:" + httpVerb);
449
	                if (httpVerb == GET) {
450
	                    doViews(format, pid);
451
	                    status = true;
452
	                }
453
                } else {
454
                    throw new InvalidRequest("0000", "No resource matched for " + resource);
455
                } 
456
                
457
                if (!status) {
458
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
459
                }
460
            } else {
461
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
462
            }
463
        } catch (BaseException be) {
464
        	// report Exceptions as clearly as possible
465
        	OutputStream out = null;
466
			try {
467
				out = response.getOutputStream();
468
			} catch (IOException e) {
469
				logMetacat.error("Could not get output stream from response", e);
470
			}
471
            serializeException(be, out);
472
        } catch (Exception e) {
473
            // report Exceptions as clearly and generically as possible
474
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
475
        	OutputStream out = null;
476
			try {
477
				out = response.getOutputStream();
478
			} catch (IOException ioe) {
479
				logMetacat.error("Could not get output stream from response", ioe);
480
			}
481
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
482
            serializeException(se, out);
483
        }
484
    }
485
    
486
    private void getToken() throws Exception {
487
		
488
		if (this.session != null) {
489
			String userId = this.session.getSubject().getValue();
490
			String fullName = null;
491
			try {
492
				Person person = this.session.getSubjectInfo().getPerson(0);
493
				fullName = person.getGivenName(0) + " " + person.getFamilyName();
494
			} catch (Exception e) {
495
				logMetacat.warn(e.getMessage(), e);
496
			}
497
			String token = null;
498
			token = TokenGenerator.getInstance().getJWT(userId, fullName);
499
			
500
			response.setStatus(200);
501
			response.setContentType("text/plain");
502
	        OutputStream out = response.getOutputStream();
503
	        out.write(token.getBytes(MetaCatServlet.DEFAULT_ENCODING));
504
	        out.close();
505
		} else {
506
			response.setStatus(401);
507
			response.setContentType("text/plain");
508
			OutputStream out = response.getOutputStream();
509
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
510
	        out.close();
511
		}
512
		
513
    }
514

    
515
    private void doQuery(String engine, String query) {
516
    	
517
		OutputStream out = null;
518

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

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

    
587
    	try {
588
    		// get a list of views
589
    		if (pid != null) {
590
    			Identifier identifier = new Identifier();
591
    			identifier.setValue(pid);
592
				InputStream stream = mnode.view(session, format, identifier);
593

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

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

    
738
        // call the service
739
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
740
        response.setStatus(200);
741
        response.setContentType("text/xml");
742
        OutputStream out = response.getOutputStream();
743
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
744
    }
745

    
746
    /**
747
     * Checks the access policy
748
     * @param id
749
     * @return
750
     * @throws ServiceFailure
751
     * @throws InvalidToken
752
     * @throws NotFound
753
     * @throws NotAuthorized
754
     * @throws NotImplemented
755
     * @throws InvalidRequest
756
     */
757
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
758
		Identifier pid = new Identifier();
759
		pid.setValue(id);
760
		Permission permission = null;
761
		try {
762
			String perm = params.get("action")[0];
763
			permission = Permission.convert(perm);
764
		} catch (Exception e) {
765
			logMetacat.warn("No permission specified");
766
		}
767
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
768
		response.setStatus(200);
769
		response.setContentType("text/xml");
770
		return result;
771
    }
772
    
773
    /**
774
     * Processes failed synchronization message
775
     * @throws NotImplemented
776
     * @throws ServiceFailure
777
     * @throws NotAuthorized
778
     * @throws InvalidRequest
779
     * @throws JiBXException
780
     * @throws IllegalAccessException 
781
     * @throws InstantiationException 
782
     * @throws IOException 
783
     * @throws InvalidToken 
784
     */
785
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException, InvalidToken {
786
    	SynchronizationFailed syncFailed = null;
787
		try {
788
			syncFailed = collectSynchronizationFailed();
789
		} catch (ParserConfigurationException e) {
790
			throw new ServiceFailure("2161", e.getMessage());
791
		} catch (SAXException e) {
792
			throw new ServiceFailure("2161", e.getMessage());
793
		}
794
		
795
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
796
    }
797

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

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

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

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

    
939
    }
940

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

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

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

    
1002
        
1003
    }
1004
    
1005
    /**
1006
     * get the logs based on passed params.  Available 
1007
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
1008
     * for more info
1009
     * @throws NotImplemented 
1010
     * @throws InvalidRequest 
1011
     * @throws NotAuthorized 
1012
     * @throws ServiceFailure 
1013
     * @throws InvalidToken 
1014
     * @throws IOException 
1015
     * @throws JiBXException 
1016
     */
1017
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
1018
    {
1019
            
1020
        Date fromDate = null;
1021
        Date toDate = null;
1022
        Event event = null;
1023
        Integer start = null;
1024
        Integer count = null;
1025
        String pidFilter = null;
1026
        
1027
        try {
1028
        	String fromDateS = params.get("fromDate")[0];
1029
            logMetacat.debug("param fromDateS: " + fromDateS);
1030
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1031
        } catch (Exception e) {
1032
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1033
        }
1034
        try {
1035
        	String toDateS = params.get("toDate")[0];
1036
            logMetacat.debug("param toDateS: " + toDateS);
1037
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1038
        } catch (Exception e) {
1039
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1040
		}
1041
        try {
1042
        	String eventS = params.get("event")[0];
1043
            event = Event.convert(eventS);
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
     * @throws InsufficientResources 
1091
     */
1092
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException, InsufficientResources {
1093
        OutputStream out = null;
1094
        
1095
        if (pid != null) { //get a specific document                
1096
            Identifier id = new Identifier();
1097
            id.setValue(pid);
1098
            InputStream data = MNodeService.getInstance(request).get(session, id);
1099
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1100
            
1101
            // set the headers for the content
1102
            String mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1103
            if (mimeType == null) {
1104
            	mimeType = "application/octet-stream";
1105
            }
1106
            String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1107
            String filename = id.getValue();
1108
            if (extension != null) {
1109
            	filename = id.getValue() + extension;
1110
            }
1111
            response.setContentType(mimeType);
1112
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1113
            out = response.getOutputStream();  
1114
            IOUtils.copyLarge(data, out);
1115
            
1116
        }
1117
        else
1118
        { //call listObjects with specified params
1119
            Date startTime = null;
1120
            Date endTime = null;
1121
            ObjectFormatIdentifier formatId = null;
1122
            boolean replicaStatus = true;
1123
            int start = 0;
1124
            //TODO: make the max count into a const
1125
            int count = 1000;
1126
            Enumeration paramlist = request.getParameterNames();
1127
            while (paramlist.hasMoreElements()) 
1128
            { //parse the params and make the crud call
1129
                String name = (String) paramlist.nextElement();
1130
                String[] value = (String[])request.getParameterValues(name);
1131

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

    
1202
    /**
1203
     * Retrieve data package as Bagit zip
1204
     * @param pid
1205
     * @throws NotImplemented 
1206
     * @throws NotFound 
1207
     * @throws NotAuthorized 
1208
     * @throws ServiceFailure 
1209
     * @throws InvalidToken 
1210
     * @throws IOException 
1211
     * @throws InvalidRequest 
1212
     */
1213
    protected void getPackage(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1214

    
1215
        Identifier id = new Identifier();
1216
        id.setValue(pid);
1217
        InputStream is = MNodeService.getInstance(request).getPackage(session, null, id);
1218
        
1219
        // use the provided filename
1220
        String filename = null;
1221
        if (is instanceof DeleteOnCloseFileInputStream) {
1222
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1223
        } else {
1224
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1225
        }
1226
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1227
        response.setContentType("application/zip");
1228
        response.setStatus(200);
1229
        OutputStream out = response.getOutputStream();
1230
        
1231
        // write it to the output stream
1232
        IOUtils.copyLarge(is, out);
1233
   }
1234
    
1235
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1236
			NotAuthorized, NotFound, NotImplemented, IOException,
1237
			JiBXException, InvalidRequest, IdentifierNotUnique,
1238
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1239

    
1240
		// publish the object
1241
		Identifier originalIdentifier = new Identifier();
1242
		originalIdentifier.setValue(pid);
1243
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1244

    
1245
		response.setStatus(200);
1246
		response.setContentType("text/xml");
1247
		OutputStream out = response.getOutputStream();
1248

    
1249
		// write new identifier to the output stream
1250
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1251
	}
1252
    
1253
    /**
1254
     * Retrieve System Metadata
1255
     * @param pid
1256
     * @throws InvalidToken
1257
     * @throws ServiceFailure
1258
     * @throws NotAuthorized
1259
     * @throws NotFound
1260
     * @throws InvalidRequest
1261
     * @throws NotImplemented
1262
     * @throws IOException
1263
     * @throws JiBXException
1264
     */
1265
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1266

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

    
1323
        InputStream object = null;
1324
        InputStream sysmeta = null;
1325
        File smFile = files.get("sysmeta");
1326
        sysmeta = new FileInputStream(smFile);
1327
        File objFile = files.get("object");
1328
        object = new FileInputStream(objFile);
1329
        
1330
        // ensure we have the object bytes
1331
        if  ( objFile == null ) {
1332
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1333
            
1334
        }
1335
        
1336
        // ensure we have the system metadata
1337
        if  ( smFile == null ) {
1338
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1339
            
1340
        }
1341
        
1342
        response.setStatus(200);
1343
        response.setContentType("text/xml");
1344
        OutputStream out = response.getOutputStream();
1345
        
1346
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1347
            // handle inserts
1348
            logMetacat.debug("Commence creation...");
1349
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1350

    
1351
            logMetacat.debug("creating object with pid " + pid.getValue());
1352
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1353
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1354
            
1355
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1356
        	// handle updates
1357
        	
1358
            // construct pids
1359
            Identifier newPid = null;
1360
            try {
1361
            	String newPidString = multipartparams.get("newPid").get(0);
1362
            	newPid = new Identifier();
1363
            	newPid.setValue(newPidString);
1364
            } catch (Exception e) {
1365
				logMetacat.error("Could not get newPid from request");
1366
			}
1367
            logMetacat.debug("Commence update...");
1368
            
1369
            // get the systemmetadata object
1370
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1371

    
1372
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1373
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1374
        } else {
1375
            throw new InvalidRequest("1000", "Operation must be create or update.");
1376
        }
1377
   
1378
    }
1379

    
1380
    /**
1381
     * Handle delete 
1382
     * @param pid ID of data object to be deleted
1383
     * @throws IOException
1384
     * @throws InvalidRequest 
1385
     * @throws NotImplemented 
1386
     * @throws NotFound 
1387
     * @throws NotAuthorized 
1388
     * @throws ServiceFailure 
1389
     * @throws InvalidToken 
1390
     * @throws JiBXException 
1391
     */
1392
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1393
    {
1394

    
1395
        OutputStream out = response.getOutputStream();
1396
        response.setStatus(200);
1397
        response.setContentType("text/xml");
1398

    
1399
        Identifier id = new Identifier();
1400
        id.setValue(pid);
1401

    
1402
        logMetacat.debug("Calling delete");
1403
        MNodeService.getInstance(request).delete(session, id);
1404
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1405
        
1406
    }
1407
    
1408
    /**
1409
     * Archives the given pid
1410
     * @param pid
1411
     * @throws InvalidToken
1412
     * @throws ServiceFailure
1413
     * @throws NotAuthorized
1414
     * @throws NotFound
1415
     * @throws NotImplemented
1416
     * @throws IOException
1417
     * @throws JiBXException
1418
     */
1419
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1420

    
1421
        OutputStream out = response.getOutputStream();
1422
        response.setStatus(200);
1423
        response.setContentType("text/xml");
1424

    
1425
        Identifier id = new Identifier();
1426
        id.setValue(pid);
1427

    
1428
        logMetacat.debug("Calling archive");
1429
        MNodeService.getInstance(request).archive(session, id);
1430
        
1431
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1432
        
1433
    }
1434

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

    
1475
}
(3-3/4)