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: tao $'
7
 *     '$Date: 2016-01-05 14:06:46 -0800 (Tue, 05 Jan 2016) $'
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
import edu.ucsb.nceas.metacat.ReadOnlyChecker;
89

    
90

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

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

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

    
146

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
913
    /**
914
     * Handle the getReplica action for the MN
915
     * @param id  the identifier for the object
916
     * @throws NotFound 
917
     * @throws ServiceFailure 
918
     * @throws NotImplemented 
919
     * @throws NotAuthorized 
920
     * @throws InvalidToken 
921
     * @throws InvalidRequest 
922
     * @throws InsufficientResources 
923
     */
924
    private void getReplica(String id) 
925
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
926
        ServiceFailure, NotFound, InsufficientResources {
927
        
928
        Identifier pid = new Identifier();
929
        pid.setValue(id);
930
        OutputStream out = null;
931
        InputStream dataBytes = null;
932
                
933
        try {
934
            // call the service
935
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
936

    
937
            response.setContentType("application/octet-stream");
938
            response.setStatus(200);
939
            out = response.getOutputStream();
940
            // write the object to the output stream
941
            IOUtils.copyLarge(dataBytes, out);
942
            
943
        } catch (IOException e) {
944
            String msg = "There was an error writing the output: " + e.getMessage();
945
            logMetacat.error(msg);
946
            throw new ServiceFailure("2181", msg);
947
        
948
        }
949

    
950
    }
951

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

    
989
        Identifier id = new Identifier();
990
        id.setValue(pid);
991

    
992
        DescribeResponse dr = null;
993
        try {
994
        	dr = MNodeService.getInstance(request).describe(session, id);
995
        } catch (BaseException e) {
996
        	response.setStatus(e.getCode());
997
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
998
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
999
            response.addHeader("DataONE-Exception-Description", e.getDescription());
1000
            response.addHeader("DataONE-Exception-PID", id.getValue());
1001
            return;
1002
		}
1003
        
1004
        response.setStatus(200);
1005
        
1006
        //response.addHeader("pid", pid);
1007
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
1008
        response.addHeader("Content-Length", dr.getContent_Length() + "");
1009
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
1010
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
1011
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
1012

    
1013
        
1014
    }
1015
    
1016
    /**
1017
     * get the logs based on passed params.  Available 
1018
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
1019
     * for more info
1020
     * @throws NotImplemented 
1021
     * @throws InvalidRequest 
1022
     * @throws NotAuthorized 
1023
     * @throws ServiceFailure 
1024
     * @throws InvalidToken 
1025
     * @throws IOException 
1026
     * @throws JiBXException 
1027
     */
1028
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
1029
    {
1030
            
1031
        Date fromDate = null;
1032
        Date toDate = null;
1033
        Event event = null;
1034
        Integer start = null;
1035
        Integer count = null;
1036
        String pidFilter = null;
1037
        
1038
        try {
1039
        	String fromDateS = params.get("fromDate")[0];
1040
            logMetacat.debug("param fromDateS: " + fromDateS);
1041
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1042
        } catch (Exception e) {
1043
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1044
        }
1045
        try {
1046
        	String toDateS = params.get("toDate")[0];
1047
            logMetacat.debug("param toDateS: " + toDateS);
1048
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1049
        } catch (Exception e) {
1050
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1051
		}
1052
        try {
1053
        	String eventS = params.get("event")[0];
1054
            event = Event.convert(eventS);
1055
        } catch (Exception e) {
1056
        	logMetacat.warn("Could not parse event: " + e.getMessage());
1057
		}
1058
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
1059
        
1060
        try {
1061
        	start =  Integer.parseInt(params.get("start")[0]);
1062
        } catch (Exception e) {
1063
			logMetacat.warn("Could not parse start: " + e.getMessage());
1064
		}
1065
        try {
1066
        	count =  Integer.parseInt(params.get("count")[0]);
1067
        } catch (Exception e) {
1068
			logMetacat.warn("Could not parse count: " + e.getMessage());
1069
		}
1070
        
1071
        try {
1072
            pidFilter = params.get("pidFilter")[0];
1073
        } catch (Exception e) {
1074
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1075
        }
1076
        
1077
        logMetacat.debug("calling getLogRecords");
1078
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1079
        
1080
        OutputStream out = response.getOutputStream();
1081
        response.setStatus(200);
1082
        response.setContentType("text/xml");
1083
        
1084
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1085
        
1086
    }
1087
    
1088
    
1089
    
1090
    /**
1091
     * Implements REST version of DataONE CRUD API --> get
1092
     * @param pid ID of data object to be read
1093
     * @throws NotImplemented 
1094
     * @throws InvalidRequest 
1095
     * @throws NotFound 
1096
     * @throws NotAuthorized 
1097
     * @throws ServiceFailure 
1098
     * @throws InvalidToken 
1099
     * @throws IOException 
1100
     * @throws JiBXException 
1101
     * @throws InsufficientResources 
1102
     */
1103
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException, InsufficientResources {
1104
        OutputStream out = null;
1105
        
1106
        if (pid != null) { //get a specific document                
1107
            Identifier id = new Identifier();
1108
            id.setValue(pid);
1109
            InputStream data = MNodeService.getInstance(request).get(session, id);
1110
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1111
            
1112
            // set the headers for the content
1113
            String mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1114
            if (mimeType == null) {
1115
            	mimeType = "application/octet-stream";
1116
            }
1117
            String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1118
            String filename = id.getValue();
1119
            if (extension != null) {
1120
            	filename = id.getValue() + extension;
1121
            }
1122
            response.setContentType(mimeType);
1123
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1124
            out = response.getOutputStream();  
1125
            IOUtils.copyLarge(data, out);
1126
            
1127
        }
1128
        else
1129
        { //call listObjects with specified params
1130
            Date startTime = null;
1131
            Date endTime = null;
1132
            ObjectFormatIdentifier formatId = null;
1133
            boolean replicaStatus = true;
1134
            int start = 0;
1135
            //TODO: make the max count into a const
1136
            int count = 1000;
1137
            Enumeration paramlist = request.getParameterNames();
1138
            while (paramlist.hasMoreElements()) 
1139
            { //parse the params and make the crud call
1140
                String name = (String) paramlist.nextElement();
1141
                String[] value = (String[])request.getParameterValues(name);
1142

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

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

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

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

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

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

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

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

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

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

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

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

    
1410
        Identifier id = new Identifier();
1411
        id.setValue(pid);
1412

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

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

    
1436
        Identifier id = new Identifier();
1437
        id.setValue(pid);
1438

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

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

    
1486
}
(3-3/4)