Project

General

Profile

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

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

    
88

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

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

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

    
144

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

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

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

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

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

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

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

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

    
408
	                String format = null;
409
	                String pid = null;
410

    
411
	                if (extra != null) {
412
		                // get the format
413
		                int formatIndex = extra.length();
414
		                if (extra.indexOf("/") > -1) {
415
		                	formatIndex = extra.indexOf("/");
416
		                }
417
		                format = extra.substring(0, formatIndex);
418
		                logMetacat.debug("view format: " + format);
419
		                
420
		                // get the pid if it is there
421
		                pid = extra.substring(formatIndex, extra.length());
422
		                if (pid != null && pid.length() == 0) {
423
		                	pid = null;
424
		                } else {
425
		                	if (pid.startsWith("/")) {
426
		                		pid = pid.substring(1);
427
		                    }
428
		                }
429
		                logMetacat.debug("pid: " + pid);
430

    
431
	                }
432
	                logMetacat.debug("verb:" + httpVerb);
433
	                if (httpVerb == GET) {
434
	                    doViews(format, pid);
435
	                    status = true;
436
	                }
437
                } 
438
                
439
                if (!status) {
440
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
441
                }
442
            } else {
443
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
444
            }
445
        } catch (BaseException be) {
446
        	// report Exceptions as clearly as possible
447
        	OutputStream out = null;
448
			try {
449
				out = response.getOutputStream();
450
			} catch (IOException e) {
451
				logMetacat.error("Could not get output stream from response", e);
452
			}
453
            serializeException(be, out);
454
        } catch (Exception e) {
455
            // report Exceptions as clearly and generically as possible
456
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
457
        	OutputStream out = null;
458
			try {
459
				out = response.getOutputStream();
460
			} catch (IOException ioe) {
461
				logMetacat.error("Could not get output stream from response", ioe);
462
			}
463
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
464
            serializeException(se, out);
465
        }
466
    }
467
    
468
    private void getToken() throws Exception {
469
		
470
		if (this.session != null) {
471
			String userId = this.session.getSubject().getValue();
472

    
473
			String token = null;
474
			token = TokenGenerator.getJWT(userId);
475
			
476
			response.setStatus(200);
477
			response.setContentType("text/plain");
478
	        OutputStream out = response.getOutputStream();
479
	        out.write(token.getBytes(MetaCatServlet.DEFAULT_ENCODING));
480
	        out.close();
481
		} else {
482
			response.setStatus(401);
483
			response.setContentType("text/plain");
484
			OutputStream out = response.getOutputStream();
485
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
486
	        out.close();
487
		}
488
		
489
    }
490

    
491
    private void doQuery(String engine, String query) {
492
    	
493
		OutputStream out = null;
494

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

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

    
563
    	try {
564
    		// get a list of views
565
    		if (pid != null) {
566
    			Identifier identifier = new Identifier();
567
    			identifier.setValue(pid);
568
				InputStream stream = mnode.view(session, format, identifier);
569

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

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

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

    
722
    /**
723
     * Checks the access policy
724
     * @param id
725
     * @return
726
     * @throws ServiceFailure
727
     * @throws InvalidToken
728
     * @throws NotFound
729
     * @throws NotAuthorized
730
     * @throws NotImplemented
731
     * @throws InvalidRequest
732
     */
733
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
734
		Identifier pid = new Identifier();
735
		pid.setValue(id);
736
		Permission permission = null;
737
		try {
738
			String perm = params.get("action")[0];
739
			permission = Permission.convert(perm);
740
		} catch (Exception e) {
741
			logMetacat.warn("No permission specified");
742
		}
743
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
744
		response.setStatus(200);
745
		response.setContentType("text/xml");
746
		return result;
747
    }
748
    
749
    /**
750
     * Processes failed synchronization message
751
     * @throws NotImplemented
752
     * @throws ServiceFailure
753
     * @throws NotAuthorized
754
     * @throws InvalidRequest
755
     * @throws JiBXException
756
     * @throws IllegalAccessException 
757
     * @throws InstantiationException 
758
     * @throws IOException 
759
     * @throws InvalidToken 
760
     */
761
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException, InvalidToken {
762
    	SynchronizationFailed syncFailed = null;
763
		try {
764
			syncFailed = collectSynchronizationFailed();
765
		} catch (ParserConfigurationException e) {
766
			throw new ServiceFailure("2161", e.getMessage());
767
		} catch (SAXException e) {
768
			throw new ServiceFailure("2161", e.getMessage());
769
		}
770
		
771
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
772
    }
773

    
774
	/**
775
     * Calculate the checksum 
776
     * @throws NotImplemented
777
     * @throws JiBXException
778
     * @throws IOException
779
     * @throws InvalidToken
780
     * @throws ServiceFailure
781
     * @throws NotAuthorized
782
     * @throws NotFound
783
     * @throws InvalidRequest
784
     */
785
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
786
    	String checksumAlgorithm = "MD5";
787
    	try {
788
    		checksumAlgorithm = PropertyService.getProperty("dataone.checksumAlgorithm.default");
789
        } catch(Exception e) {
790
        	logMetacat.warn("Could not lookup configured default checksum algorithm, using: " + checksumAlgorithm);
791
        }    
792
        
793
        Identifier pidid = new Identifier();
794
        pidid.setValue(pid);
795
        try {
796
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
797
        } catch(Exception e) {
798
            //do nothing.  use the default
799
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
800
        }
801
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
802
        
803
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
804
        logMetacat.debug("got checksum " + c.getValue());
805
        response.setStatus(200);
806
        logMetacat.debug("serializing response");
807
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
808
        logMetacat.debug("done serializing response.");
809
        
810
    }
811
    
812
	/**
813
     * handle the replicate action for MN
814
	 * @throws JiBXException 
815
	 * @throws FileUploadException 
816
	 * @throws IOException 
817
	 * @throws InvalidRequest 
818
	 * @throws ServiceFailure 
819
	 * @throws UnsupportedType 
820
	 * @throws InsufficientResources 
821
	 * @throws NotAuthorized 
822
	 * @throws NotImplemented 
823
	 * @throws IllegalAccessException 
824
	 * @throws InstantiationException 
825
	 * @throws InvalidToken 
826
     */
827
    private void replicate() 
828
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException, 
829
        JiBXException, NotImplemented, NotAuthorized, InsufficientResources, 
830
        UnsupportedType, InstantiationException, IllegalAccessException, InvalidToken {
831

    
832
        logMetacat.debug("in POST replicate()");
833
        
834
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
835
        boolean allowed = false;
836
        if (session == null) {
837
        	String msg = "No session was provided.";
838
            NotAuthorized failure = new NotAuthorized("2152", msg);
839
        	throw failure;
840
        } else {
841
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
842
        	if (!allowed) {
843
        		String msg = "User is not an admin user";
844
                NotAuthorized failure = new NotAuthorized("2152", msg);
845
            	throw failure;
846
        	}
847
        }
848
        
849
        // parse the systemMetadata
850
        Map<String, File> files = collectMultipartFiles();        
851
        final SystemMetadata sysmeta = TypeMarshaller.unmarshalTypeFromFile(SystemMetadata.class, files.get("sysmeta"));
852
        
853
        String sn = multipartparams.get("sourceNode").get(0);
854
        logMetacat.debug("sourceNode: " + sn);
855
        final NodeReference sourceNode = new NodeReference();
856
        sourceNode.setValue(sn);
857
        
858
        // run it in a thread to avoid connection timeout
859
        Runnable runner = new Runnable() {
860
			@Override
861
			public void run() {
862
				try {
863
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
864
				} catch (Exception e) {
865
					logMetacat.error("Error running replication: " + e.getMessage(), e);
866
					throw new RuntimeException(e.getMessage(), e);
867
				}
868
			}
869
    	};
870
    	// submit the task, and that's it
871
    	executor.submit(runner);
872
        
873
    	// thread was started, so we return success
874
        response.setStatus(200);
875
        
876
    }
877

    
878
    /**
879
     * Handle the getReplica action for the MN
880
     * @param id  the identifier for the object
881
     * @throws NotFound 
882
     * @throws ServiceFailure 
883
     * @throws NotImplemented 
884
     * @throws NotAuthorized 
885
     * @throws InvalidToken 
886
     * @throws InvalidRequest 
887
     * @throws InsufficientResources 
888
     */
889
    private void getReplica(String id) 
890
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
891
        ServiceFailure, NotFound, InsufficientResources {
892
        
893
        Identifier pid = new Identifier();
894
        pid.setValue(id);
895
        OutputStream out = null;
896
        InputStream dataBytes = null;
897
                
898
        try {
899
            // call the service
900
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
901

    
902
            response.setContentType("application/octet-stream");
903
            response.setStatus(200);
904
            out = response.getOutputStream();
905
            // write the object to the output stream
906
            IOUtils.copyLarge(dataBytes, out);
907
            
908
        } catch (IOException e) {
909
            String msg = "There was an error writing the output: " + e.getMessage();
910
            logMetacat.error(msg);
911
            throw new ServiceFailure("2181", msg);
912
        
913
        }
914

    
915
    }
916

    
917
    /**
918
     * Get the Node information
919
     * 
920
     * @throws JiBXException
921
     * @throws IOException
922
     * @throws InvalidRequest 
923
     * @throws ServiceFailure 
924
     * @throws NotAuthorized 
925
     * @throws NotImplemented 
926
     */
927
    private void node() 
928
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
929
        
930
        Node n = MNodeService.getInstance(request).getCapabilities();
931
        
932
        response.setContentType("text/xml");
933
        response.setStatus(200);
934
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
935
        
936
    }
937
    
938
    /**
939
     * MN_crud.describe()
940
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
941
     * @param pid
942
     * @throws InvalidRequest 
943
     * @throws NotImplemented 
944
     * @throws NotFound 
945
     * @throws NotAuthorized 
946
     * @throws ServiceFailure 
947
     * @throws InvalidToken 
948
     */
949
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
950
    {
951
        
952
        response.setContentType("text/xml");
953

    
954
        Identifier id = new Identifier();
955
        id.setValue(pid);
956

    
957
        DescribeResponse dr = null;
958
        try {
959
        	dr = MNodeService.getInstance(request).describe(session, id);
960
        } catch (BaseException e) {
961
        	response.setStatus(e.getCode());
962
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
963
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
964
            response.addHeader("DataONE-Exception-Description", e.getDescription());
965
            response.addHeader("DataONE-Exception-PID", id.getValue());
966
            return;
967
		}
968
        
969
        response.setStatus(200);
970
        
971
        //response.addHeader("pid", pid);
972
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
973
        response.addHeader("Content-Length", dr.getContent_Length() + "");
974
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
975
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
976
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
977

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

    
1092
            out = response.getOutputStream();  
1093
            IOUtils.copyLarge(data, out);
1094
            
1095
        }
1096
        else
1097
        { //call listObjects with specified params
1098
            Date startTime = null;
1099
            Date endTime = null;
1100
            ObjectFormatIdentifier formatId = null;
1101
            boolean replicaStatus = true;
1102
            int start = 0;
1103
            //TODO: make the max count into a const
1104
            int count = 1000;
1105
            Enumeration paramlist = request.getParameterNames();
1106
            while (paramlist.hasMoreElements()) 
1107
            { //parse the params and make the crud call
1108
                String name = (String) paramlist.nextElement();
1109
                String[] value = (String[])request.getParameterValues(name);
1110

    
1111
                if (name.equals("fromDate") && value != null)
1112
                {
1113
                    try
1114
                    {
1115
                      //startTime = dateFormat.parse(value[0]);
1116
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1117
                        //startTime = parseDateAndConvertToGMT(value[0]);
1118
                    }
1119
                    catch(Exception e)
1120
                    {  //if we can't parse it, just don't use the fromDate param
1121
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1122
                        startTime = null;
1123
                    }
1124
                }
1125
                else if(name.equals("toDate") && value != null)
1126
                {
1127
                    try
1128
                    {
1129
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1130
                    }
1131
                    catch(Exception e)
1132
                    {  //if we can't parse it, just don't use the toDate param
1133
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1134
                        endTime = null;
1135
                    }
1136
                }
1137
                else if(name.equals("formatId") && value != null) 
1138
                {
1139
                	formatId = new ObjectFormatIdentifier();
1140
                	formatId.setValue(value[0]);
1141
                }
1142
                else if(name.equals("replicaStatus") && value != null)
1143
                {
1144
                    if(value != null && 
1145
                       value.length > 0 && 
1146
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1147
                    {
1148
                        replicaStatus = false;
1149
                    }
1150
                }
1151
                else if(name.equals("start") && value != null)
1152
                {
1153
                    start = new Integer(value[0]).intValue();
1154
                }
1155
                else if(name.equals("count") && value != null)
1156
                {
1157
                    count = new Integer(value[0]).intValue();
1158
                }
1159
            }
1160
            //make the crud call
1161
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1162
                    " endTime: " + endTime + " formatId: " + 
1163
                    formatId + " replicaStatus: " + replicaStatus + 
1164
                    " start: " + start + " count: " + count);
1165
           
1166
            ObjectList ol = 
1167
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1168
           			formatId, replicaStatus, start, count);
1169
           
1170
            out = response.getOutputStream();  
1171
            response.setStatus(200);
1172
            response.setContentType("text/xml");
1173
            // Serialize and write it to the output stream
1174
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1175
            
1176
        }
1177
        
1178
    }
1179
    
1180

    
1181
    /**
1182
     * Retrieve data package as Bagit zip
1183
     * @param pid
1184
     * @throws NotImplemented 
1185
     * @throws NotFound 
1186
     * @throws NotAuthorized 
1187
     * @throws ServiceFailure 
1188
     * @throws InvalidToken 
1189
     * @throws IOException 
1190
     * @throws InvalidRequest 
1191
     */
1192
    protected void getPackage(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1193

    
1194
        Identifier id = new Identifier();
1195
        id.setValue(pid);
1196
        InputStream is = MNodeService.getInstance(request).getPackage(session, null, id);
1197
        
1198
        // use the provided filename
1199
        String filename = null;
1200
        if (is instanceof DeleteOnCloseFileInputStream) {
1201
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1202
        } else {
1203
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1204
        }
1205
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1206
        response.setContentType("application/zip");
1207
        response.setStatus(200);
1208
        OutputStream out = response.getOutputStream();
1209
        
1210
        // write it to the output stream
1211
        IOUtils.copyLarge(is, out);
1212
   }
1213
    
1214
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1215
			NotAuthorized, NotFound, NotImplemented, IOException,
1216
			JiBXException, InvalidRequest, IdentifierNotUnique,
1217
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1218

    
1219
		// publish the object
1220
		Identifier originalIdentifier = new Identifier();
1221
		originalIdentifier.setValue(pid);
1222
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1223

    
1224
		response.setStatus(200);
1225
		response.setContentType("text/xml");
1226
		OutputStream out = response.getOutputStream();
1227

    
1228
		// write new identifier to the output stream
1229
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1230
	}
1231
    
1232
    /**
1233
     * Retrieve System Metadata
1234
     * @param pid
1235
     * @throws InvalidToken
1236
     * @throws ServiceFailure
1237
     * @throws NotAuthorized
1238
     * @throws NotFound
1239
     * @throws InvalidRequest
1240
     * @throws NotImplemented
1241
     * @throws IOException
1242
     * @throws JiBXException
1243
     */
1244
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1245

    
1246
        Identifier id = new Identifier();
1247
        id.setValue(pid);
1248
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1249
        
1250
        response.setContentType("text/xml");
1251
        response.setStatus(200);
1252
        OutputStream out = response.getOutputStream();
1253
        
1254
        // Serialize and write it to the output stream
1255
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1256
   }
1257
    
1258
    
1259
    /**
1260
     * Inserts or updates the object
1261
     * 
1262
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1263
     *               is the existing pid.  If insert, the pid is the new one
1264
     * @throws InvalidRequest 
1265
     * @throws ServiceFailure 
1266
     * @throws JiBXException 
1267
     * @throws NotImplemented 
1268
     * @throws InvalidSystemMetadata 
1269
     * @throws InsufficientResources 
1270
     * @throws UnsupportedType 
1271
     * @throws IdentifierNotUnique 
1272
     * @throws NotAuthorized 
1273
     * @throws InvalidToken 
1274
     * @throws NotFound 
1275
     * @throws IOException 
1276
     * @throws IllegalAccessException 
1277
     * @throws InstantiationException 
1278
     */
1279
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1280
       
1281
    	// Read the incoming data from its Mime Multipart encoding
1282
    	Map<String, File> files = collectMultipartFiles();
1283
               
1284
    	Identifier pid = new Identifier();
1285
        if (trailingPid == null) {
1286
	        // get the pid string from the body and set the value
1287
	        String pidString = multipartparams.get("pid").get(0);
1288
	        if (pidString != null) {
1289
            pid.setValue(pidString);
1290
            
1291
          } else {
1292
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1293
              
1294
          }
1295
        } else {
1296
        	// use the pid included in the URL
1297
        	pid.setValue(trailingPid);
1298
        }
1299
        logMetacat.debug("putObject with pid " + pid.getValue());
1300
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1301

    
1302
        InputStream object = null;
1303
        InputStream sysmeta = null;
1304
        File smFile = files.get("sysmeta");
1305
        sysmeta = new FileInputStream(smFile);
1306
        File objFile = files.get("object");
1307
        object = new FileInputStream(objFile);
1308
        
1309
        // ensure we have the object bytes
1310
        if  ( objFile == null ) {
1311
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1312
            
1313
        }
1314
        
1315
        // ensure we have the system metadata
1316
        if  ( smFile == null ) {
1317
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1318
            
1319
        }
1320
        
1321
        response.setStatus(200);
1322
        response.setContentType("text/xml");
1323
        OutputStream out = response.getOutputStream();
1324
        
1325
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1326
            // handle inserts
1327
            logMetacat.debug("Commence creation...");
1328
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1329

    
1330
            logMetacat.debug("creating object with pid " + pid.getValue());
1331
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1332
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1333
            
1334
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1335
        	// handle updates
1336
        	
1337
            // construct pids
1338
            Identifier newPid = null;
1339
            try {
1340
            	String newPidString = multipartparams.get("newPid").get(0);
1341
            	newPid = new Identifier();
1342
            	newPid.setValue(newPidString);
1343
            } catch (Exception e) {
1344
				logMetacat.error("Could not get newPid from request");
1345
			}
1346
            logMetacat.debug("Commence update...");
1347
            
1348
            // get the systemmetadata object
1349
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1350

    
1351
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1352
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1353
        } else {
1354
            throw new InvalidRequest("1000", "Operation must be create or update.");
1355
        }
1356
   
1357
    }
1358

    
1359
    /**
1360
     * Handle delete 
1361
     * @param pid ID of data object to be deleted
1362
     * @throws IOException
1363
     * @throws InvalidRequest 
1364
     * @throws NotImplemented 
1365
     * @throws NotFound 
1366
     * @throws NotAuthorized 
1367
     * @throws ServiceFailure 
1368
     * @throws InvalidToken 
1369
     * @throws JiBXException 
1370
     */
1371
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1372
    {
1373

    
1374
        OutputStream out = response.getOutputStream();
1375
        response.setStatus(200);
1376
        response.setContentType("text/xml");
1377

    
1378
        Identifier id = new Identifier();
1379
        id.setValue(pid);
1380

    
1381
        logMetacat.debug("Calling delete");
1382
        MNodeService.getInstance(request).delete(session, id);
1383
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1384
        
1385
    }
1386
    
1387
    /**
1388
     * Archives the given pid
1389
     * @param pid
1390
     * @throws InvalidToken
1391
     * @throws ServiceFailure
1392
     * @throws NotAuthorized
1393
     * @throws NotFound
1394
     * @throws NotImplemented
1395
     * @throws IOException
1396
     * @throws JiBXException
1397
     */
1398
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1399

    
1400
        OutputStream out = response.getOutputStream();
1401
        response.setStatus(200);
1402
        response.setContentType("text/xml");
1403

    
1404
        Identifier id = new Identifier();
1405
        id.setValue(pid);
1406

    
1407
        logMetacat.debug("Calling archive");
1408
        MNodeService.getInstance(request).archive(session, id);
1409
        
1410
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1411
        
1412
    }
1413

    
1414
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1415
		
1416
		// Read the incoming data from its Mime Multipart encoding
1417
		logMetacat.debug("Disassembling MIME multipart form");
1418
		InputStream sf = null;
1419
	
1420
		// handle MMP inputs
1421
		File tmpDir = getTempDirectory();
1422
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1423
		MultipartRequestResolver mrr = 
1424
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1425
		MultipartRequest mr = null;
1426
		try {
1427
			mr = mrr.resolveMultipart(request);
1428
		} catch (Exception e) {
1429
			throw new ServiceFailure("2161", 
1430
					"Could not resolve multipart: " + e.getMessage());
1431
		}
1432
		logMetacat.debug("resolved multipart request");
1433
		Map<String, File> files = mr.getMultipartFiles();
1434
		if (files == null || files.keySet() == null) {
1435
			throw new InvalidRequest("2163",
1436
					"must have multipart file with name 'message'");
1437
		}
1438
		logMetacat.debug("got multipart files");
1439
	
1440
		multipartparams = mr.getMultipartParameters();
1441
	
1442
		File sfFile = files.get("message");
1443
		if (sfFile == null) {
1444
			throw new InvalidRequest("2163",
1445
					"Missing the required file-part 'message' from the multipart request.");
1446
		}
1447
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1448
		sf = new FileInputStream(sfFile);
1449
	
1450
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1451
		return syncFailed;
1452
	}
1453

    
1454
}
(3-3/4)