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-11-28 11:40:33 -0800 (Mon, 28 Nov 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.v2;
24

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

    
38
import javax.servlet.ServletContext;
39
import javax.servlet.http.HttpServletRequest;
40
import javax.servlet.http.HttpServletResponse;
41
import javax.xml.parsers.ParserConfigurationException;
42

    
43
import org.apache.commons.fileupload.FileUploadException;
44
import org.apache.commons.io.IOUtils;
45
import org.apache.log4j.Logger;
46
import org.dataone.client.v2.formats.ObjectFormatCache;
47
import org.dataone.client.v2.formats.ObjectFormatInfo;
48
import org.dataone.exceptions.MarshallingException;
49
import org.dataone.mimemultipart.MultipartRequest;
50
import org.dataone.mimemultipart.MultipartRequestResolver;
51
import org.dataone.portal.TokenGenerator;
52
import org.dataone.service.exceptions.BaseException;
53
import org.dataone.service.exceptions.IdentifierNotUnique;
54
import org.dataone.service.exceptions.InsufficientResources;
55
import org.dataone.service.exceptions.InvalidRequest;
56
import org.dataone.service.exceptions.InvalidSystemMetadata;
57
import org.dataone.service.exceptions.InvalidToken;
58
import org.dataone.service.exceptions.NotAuthorized;
59
import org.dataone.service.exceptions.NotFound;
60
import org.dataone.service.exceptions.NotImplemented;
61
import org.dataone.service.exceptions.ServiceFailure;
62
import org.dataone.service.exceptions.SynchronizationFailed;
63
import org.dataone.service.exceptions.UnsupportedType;
64
import org.dataone.service.types.v1.Checksum;
65
import org.dataone.service.types.v1.DescribeResponse;
66
import org.dataone.service.types.v1.Identifier;
67
import org.dataone.service.types.v1.NodeReference;
68
import org.dataone.service.types.v1.ObjectFormatIdentifier;
69
import org.dataone.service.types.v1.ObjectList;
70
import org.dataone.service.types.v1.Permission;
71
import org.dataone.service.types.v1.Person;
72
import org.dataone.service.types.v1.Subject;
73
import org.dataone.service.types.v1.SubjectInfo;
74
import org.dataone.service.types.v1_1.QueryEngineDescription;
75
import org.dataone.service.types.v1_1.QueryEngineList;
76
import org.dataone.service.types.v2.Log;
77
import org.dataone.service.types.v2.MediaType;
78
import org.dataone.service.types.v2.MediaTypeProperty;
79
import org.dataone.service.types.v2.Node;
80
import org.dataone.service.types.v2.ObjectFormat;
81
import org.dataone.service.types.v2.OptionList;
82
import org.dataone.service.types.v2.SystemMetadata;
83
import org.dataone.service.util.Constants;
84
import org.dataone.service.util.DateTimeMarshaller;
85
import org.dataone.service.util.ExceptionHandler;
86
import org.dataone.service.util.TypeMarshaller;
87
import org.xml.sax.SAXException;
88

    
89
import edu.ucsb.nceas.metacat.MetaCatServlet;
90
import edu.ucsb.nceas.metacat.ReadOnlyChecker;
91
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeInputStream;
92
import edu.ucsb.nceas.metacat.dataone.MNodeService;
93
import edu.ucsb.nceas.metacat.properties.PropertyService;
94
import edu.ucsb.nceas.metacat.restservice.D1ResourceHandler;
95
import edu.ucsb.nceas.metacat.util.DeleteOnCloseFileInputStream;
96
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
97

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

    
129
 *    systemMetadataChanged() - POST /dirtySystemMetadata/PID
130
 * 	
131
 * 	MNReplication
132
 * 		replicate() - POST /d1/mn/replicate
133
 *    getReplica() - GET /d1/mn/replica
134
 * 
135
 * ******************
136
 * @author leinfelder
137
 *
138
 */
139
public class MNResourceHandler extends D1ResourceHandler {
140

    
141
    // MN-specific API Resources
142
    protected static final String RESOURCE_MONITOR = "monitor";
143
    protected static final String RESOURCE_REPLICATE = "replicate";
144
    protected static final String RESOURCE_REPLICAS = "replica";
145
    protected static final String RESOURCE_NODE = "node";
146
    protected static final String RESOURCE_ERROR = "error";
147
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
148
    protected static final String RESOURCE_GENERATE_ID = "generate";
149
    protected static final String RESOURCE_PUBLISH = "publish";
150
    protected static final String RESOURCE_PACKAGE = "packages";
151
    protected static final String RESOURCE_TOKEN = "token";
152
    protected static final String RESOURCE_WHOAMI = "whoami";
153

    
154

    
155

    
156
    
157
    // shared executor
158
	private static ExecutorService executor = null;
159

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

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

    
228
                if (resource.startsWith(RESOURCE_NODE)) {
229
                    // node response
230
                    node();
231
                    status = true;
232
                } else if (resource.startsWith(RESOURCE_TOKEN)) {
233
                    logMetacat.debug("Using resource 'token'");
234
                    // get
235
                    if (httpVerb == GET) {
236
                    	// after the command
237
                        getToken();
238
                        status = true;
239
                    }
240
                    
241
                } else if (resource.startsWith(RESOURCE_WHOAMI)) {
242
                    logMetacat.debug("Using resource 'whoami'");
243
                    // get
244
                    if (httpVerb == GET) {
245
                    	// after the command
246
                        whoami();
247
                        status = true;
248
                    }
249
                    
250
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
251
                    if (httpVerb == GET) {
252
                    	// after the command
253
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
254
                        extra = decode(extra);
255
	                	// check the access rules
256
	                    isAuthorized(extra);
257
	                    status = true;
258
	                    logMetacat.debug("done getting access");
259
                    }
260
                } else if (resource.startsWith(RESOURCE_META)) {
261
                    logMetacat.debug("Using resource 'meta'");
262
                    // get
263
                    if (httpVerb == GET) {
264
                        logMetacat.debug("Using resource 'meta' for GET");
265
                    	// after the command
266
                        extra = parseTrailing(resource, RESOURCE_META);
267
                        extra = decode(extra);
268
                        getSystemMetadataObject(extra);
269
                        status = true;
270
                    } else if (httpVerb == PUT) {
271
                        logMetacat.debug("Using resource 'meta' for PUT");
272
                        updateSystemMetadata();
273
                        status = true;
274
                    }
275
                    
276
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
277
                    logMetacat.debug("Using resource 'object'");
278
                    // after the command
279
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
280
                    extra = decode(extra);
281
                    logMetacat.debug("objectId: " + extra);
282
                    logMetacat.debug("verb:" + httpVerb);
283

    
284
                    if (httpVerb == GET) {
285
                        getObject(extra);
286
                        status = true;
287
                    } else if (httpVerb == POST) {
288
                    	// part of the params, not the URL
289
                        putObject(null, FUNCTION_NAME_INSERT);
290
                        status = true;
291
                    } else if (httpVerb == PUT) {
292
                        putObject(extra, FUNCTION_NAME_UPDATE);
293
                        status = true;
294
                    } else if (httpVerb == DELETE) {
295
                        deleteObject(extra);
296
                        status = true;
297
                    } else if (httpVerb == HEAD) {
298
                        describeObject(extra);
299
                        status = true;
300
                    }
301
                  
302
                } else if (resource.startsWith(RESOURCE_LOG)) {
303
                    logMetacat.debug("Using resource 'log'");
304
                    // handle log events
305
                    if (httpVerb == GET) {
306
                        getLog();
307
                        status = true;
308
                    }
309
                } else if (resource.startsWith(Constants.RESOURCE_ARCHIVE)) {
310
                    logMetacat.debug("Using resource " + Constants.RESOURCE_ARCHIVE);
311
                    // handle archive events
312
                    if (httpVerb == PUT) {
313
                        extra = parseTrailing(resource, Constants.RESOURCE_ARCHIVE);
314
                        extra = decode(extra);
315
                        archive(extra);
316
                        status = true;
317
                    }
318
                } else if (resource.startsWith(Constants.RESOURCE_CHECKSUM)) {
319
                    logMetacat.debug("Using resource 'checksum'");
320
                    // handle checksum requests
321
                    if (httpVerb == GET) {
322
                    	// after the command
323
                        extra = parseTrailing(resource, Constants.RESOURCE_CHECKSUM);
324
                        extra = decode(extra);
325
                        checksum(extra);
326
                        status = true;
327
                    }
328
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
329
                    // there are various parts to monitoring
330
                    if (httpVerb == GET) {
331
                    	// after the command
332
                        extra = parseTrailing(resource, RESOURCE_MONITOR);
333
                        extra = decode(extra);
334
                        // ping
335
                        if (extra.toLowerCase().equals("ping")) {
336
                            logMetacat.debug("processing ping request");
337
                            Date result = MNodeService.getInstance(request).ping();
338
                            // TODO: send to output	
339
                            status = true;
340
                            
341
                        }
342
                        
343
                    }
344
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
345
                	if (httpVerb == POST) {
346
	                    logMetacat.debug("processing replicate request");
347
	                    replicate();
348
	                    status = true;
349
                	}
350
                } else if (resource.startsWith(RESOURCE_ERROR)) {
351
	                // sync error
352
	                if (httpVerb == POST) {
353
	                    syncError();
354
	                    status = true;
355
	                }
356
                } else if (resource.startsWith(RESOURCE_META_CHANGED)) {
357
                    // system metadata changed
358
                    if (httpVerb == POST) {
359
                        systemMetadataChanged();
360
                        status = true;
361
                    }
362
                } else if (resource.startsWith(RESOURCE_REPLICAS)) {
363
                    // get replica
364
                    if (httpVerb == GET) {
365
                        extra = parseTrailing(resource, RESOURCE_REPLICAS);
366
                        extra = decode(extra);
367
                        getReplica(extra);
368
                        status = true;
369
                    }
370
                } else if (resource.startsWith(RESOURCE_QUERY)) {
371
	                logMetacat.debug("Using resource " + RESOURCE_QUERY);
372
	                // after the command
373
	                extra = parseTrailing(resource, RESOURCE_QUERY);
374
	                logMetacat.debug("query extra: " + extra);
375

    
376
	                String engine = null;
377
	                String query = null;
378

    
379
	                if (extra != null) {
380
		                // get the engine
381
		                int engineIndex = extra.length();
382
		                if (extra.indexOf("/") > -1) {
383
		                	engineIndex = extra.indexOf("/");
384
		                }
385
		                engine = extra.substring(0, engineIndex);
386
		                engine = decode(engine);
387
		                logMetacat.debug("query engine: " + engine);
388
		                
389
		                // check the query string first
390
		                query = request.getQueryString();
391
		                
392
		                // if null, look at the whole endpoint
393
		                if (query == null) {
394
			                // get the query if it is there
395
			                query = extra.substring(engineIndex, extra.length());
396
			                if (query != null && query.length() == 0) {
397
			                	query = null;
398
			                } else {
399
			                	if (query.startsWith("/")) {
400
			                		query = query.substring(1);
401
			                    }
402
			                }
403
		                }
404
		                
405
		                query = decode(query);
406
		                logMetacat.debug("query: " + query);
407

    
408
	                }
409
	                logMetacat.debug("verb:" + httpVerb);
410
	                if (httpVerb == GET) {
411
	                    doQuery(engine, query);
412
	                    status = true;
413
	                }
414
                } else if (resource.startsWith(RESOURCE_GENERATE_ID)) {
415
                	// generate an id
416
                    if (httpVerb == POST) {
417
                        generateIdentifier();
418
                        status = true;
419
                    }
420
                } else if (resource.startsWith(RESOURCE_PUBLISH)) {
421
                    logMetacat.debug("Using resource: " + RESOURCE_PUBLISH);
422
                    // PUT
423
                    if (httpVerb == PUT) {
424
                    	// after the command
425
                        extra = parseTrailing(resource, RESOURCE_PUBLISH);
426
                        extra = decode(extra);
427
                        publish(extra);
428
                        status = true;
429
                    }  
430
                } else if (resource.startsWith(RESOURCE_PACKAGE)) {
431
                    logMetacat.debug("Using resource: " + RESOURCE_PACKAGE);
432
                    // after the command
433
                    extra = parseTrailing(resource, RESOURCE_PACKAGE);
434
                    
435
                    String format = null;
436
	                String pid = null;
437

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

    
460
	                }
461
                    
462
                    // get
463
                    if (httpVerb == GET) {
464
                        
465
                        getPackage(format, pid);
466
                        status = true;
467
                    }  
468
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
469
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
470
	                // after the command
471
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
472
	                logMetacat.debug("view extra: " + extra);
473

    
474
	                String format = null;
475
	                String pid = null;
476

    
477
	                if (extra != null) {
478
		                // get the format
479
		                int formatIndex = extra.length();
480
		                if (extra.indexOf("/") > -1) {
481
		                	formatIndex = extra.indexOf("/");
482
		                }
483
		                format = extra.substring(0, formatIndex);
484
		                format = decode(format);
485
		                logMetacat.debug("view format: " + format);
486
		                
487
		                // get the pid if it is there
488
		                pid = extra.substring(formatIndex, extra.length());
489
		                if (pid != null && pid.length() == 0) {
490
		                	pid = null;
491
		                } else {
492
		                	if (pid.startsWith("/")) {
493
		                		pid = pid.substring(1);
494
		                    }
495
		                }
496
		                pid = decode(pid);
497
		                logMetacat.debug("pid: " + pid);
498

    
499
	                }
500
	                logMetacat.debug("verb:" + httpVerb);
501
	                if (httpVerb == GET) {
502
	                    doViews(format, pid);
503
	                    status = true;
504
	                }
505
                } else {
506
                    throw new InvalidRequest("0000", "No resource matched for " + resource);
507
                }
508
                
509
                if (!status) {
510
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
511
                }
512
            } else {
513
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
514
            }
515
        } catch (BaseException be) {
516
        	// report Exceptions as clearly as possible
517
        	OutputStream out = null;
518
			try {
519
				out = response.getOutputStream();
520
			} catch (IOException e) {
521
				logMetacat.error("Could not get output stream from response", e);
522
			}
523
            serializeException(be, out);
524
        } catch (Exception e) {
525
            // report Exceptions as clearly and generically as possible
526
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
527
        	OutputStream out = null;
528
			try {
529
				out = response.getOutputStream();
530
			} catch (IOException ioe) {
531
				logMetacat.error("Could not get output stream from response", ioe);
532
			}
533
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
534
            serializeException(se, out);
535
        }
536
    }
537
    
538

    
539
    private void doQuery(String engine, String query) {
540
    	
541
		OutputStream out = null;
542

    
543
    	try {
544
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter			
545
	    	if (engine == null) {
546
	    		// just looking for list of engines
547
	    		MNodeService mnode = MNodeService.getInstance(request);
548
    			mnode.setSession(session);
549
	    		QueryEngineList qel = mnode.listQueryEngines(session);
550
	    		response.setContentType("text/xml");
551
	            response.setStatus(200);
552
	            out = response.getOutputStream();
553
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
554
	            return;
555
	    	} else {
556
	    		if (query != null) {
557
	    			MNodeService mnode = MNodeService.getInstance(request);
558
	    			mnode.setSession(session);
559
	    			InputStream stream = mnode.query(session, engine, query);
560

    
561
	    			// set the content-type if we have it from the implementation
562
	    			if (stream instanceof ContentTypeInputStream) {
563
		    			//response.setContentType("application/octet-stream");
564
		    			//response.setContentType("text/xml");
565
	    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
566
	    			}
567
	                response.setStatus(200);
568
	                out = response.getOutputStream();
569
	                // write the results to the output stream
570
	                IOUtils.copyLarge(stream, out);
571
	                return;
572
	    		} else {
573
	    			MNodeService mnode = MNodeService.getInstance(request);
574
	    			mnode.setSession(session);
575
	    			QueryEngineDescription qed = mnode.getQueryEngineDescription(session, engine);
576
	    			response.setContentType("text/xml");
577
		            response.setStatus(200);
578
		            out = response.getOutputStream();
579
		            TypeMarshaller.marshalTypeToOutputStream(qed, out);
580
		            return;
581
	    		}
582
	    	}
583
	        
584
	        
585
    	} catch (BaseException be) {
586
        	// report Exceptions as clearly as possible
587
			try {
588
				out = response.getOutputStream();
589
			} catch (IOException e) {
590
				logMetacat.error("Could not get output stream from response", e);
591
			}
592
            serializeException(be, out);
593
        } catch (Exception e) {
594
            // report Exceptions as clearly and generically as possible
595
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
596
			try {
597
				out = response.getOutputStream();
598
			} catch (IOException ioe) {
599
				logMetacat.error("Could not get output stream from response", ioe);
600
			}
601
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
602
            serializeException(se, out);
603
        }
604
    }
605
    
606
    private void doViews(String format, String pid) {
607
    	
608
		OutputStream out = null;
609
		MNodeService mnode = MNodeService.getInstance(request);
610

    
611
    	try {
612
    		// get a list of views
613
    		if (pid != null) {
614
    			Identifier identifier = new Identifier();
615
    			identifier.setValue(pid);
616
				InputStream stream = mnode.view(session, format, identifier);
617

    
618
    			// set the content-type if we have it from the implementation
619
    			if (stream instanceof ContentTypeInputStream) {
620
    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
621
    			}
622
                response.setStatus(200);
623
                out = response.getOutputStream();
624
                // write the results to the output stream
625
                IOUtils.copyLarge(stream, out);
626
                return;
627
    		} else {
628
    			// TODO: list the registered views
629
                //BaseException ni = new NotImplemented("9999", "MN.listViews() is not implemented at this node");
630
				//throw ni;
631
    		    OptionList list = mnode.listViews(session);
632
    	        
633
    	        response.setContentType("text/xml");
634
    	        response.setStatus(200);
635
    	        TypeMarshaller.marshalTypeToOutputStream(list, response.getOutputStream());
636
    		}
637
	    	
638
	        
639
    	} catch (BaseException be) {
640
        	// report Exceptions as clearly as possible
641
			try {
642
				out = response.getOutputStream();
643
			} catch (IOException e) {
644
				logMetacat.error("Could not get output stream from response", e);
645
			}
646
            serializeException(be, out);
647
        } catch (Exception e) {
648
            // report Exceptions as clearly and generically as possible
649
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
650
			try {
651
				out = response.getOutputStream();
652
			} catch (IOException ioe) {
653
				logMetacat.error("Could not get output stream from response", ioe);
654
			}
655
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
656
            serializeException(se, out);
657
        }
658
    }
659
    
660
    /**
661
     * Handles notification of system metadata changes for the given identifier
662
     * 
663
     * @param id  the identifier for the object
664
     * @throws InvalidToken 
665
     * @throws InvalidRequest 
666
     * @throws NotAuthorized 
667
     * @throws ServiceFailure 
668
     * @throws NotImplemented 
669
     */
670
    private void systemMetadataChanged() 
671
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
672
        InvalidToken {
673
        
674
        ReadOnlyChecker checker = new ReadOnlyChecker();
675
        boolean isReadOnlyMode = checker.isReadOnly();
676
        if(isReadOnlyMode) {
677
            throw new ServiceFailure("1333", ReadOnlyChecker.DATAONEERROR);
678
        }
679
        
680
        //final long serialVersion = 0L;
681
        String serialVersionStr = null;
682
        String dateSysMetaLastModifiedStr = null;
683
        
684
        // mkae sure we have the multipart params
685
        try {
686
			initMultipartParams();
687
		} catch (Exception e1) {
688
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
689
		}
690
        
691
        // get the pid
692
        String id = null;
693
        try {
694
        	id = multipartparams.get("pid").get(0);
695
        } catch (NullPointerException e) {
696
            String msg = "The 'pid' must be provided as a parameter and was not.";
697
            logMetacat.error(msg);
698
            throw new InvalidRequest("1334", msg);
699
        }  
700
        final Identifier pid = new Identifier();
701
        pid.setValue(id);
702
        
703
        // get the serialVersion
704
        try {
705
            serialVersionStr = multipartparams.get("serialVersion").get(0);
706
        } catch (NullPointerException e) {
707
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
708
            logMetacat.error(msg);
709
            throw new InvalidRequest("1334", msg);
710
            
711
        }  
712
        
713
        final long serialVersion = (new Long(serialVersionStr)).longValue();
714
        
715
        // get the dateSysMetaLastModified
716
        try {
717
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
718
            
719
            
720
        } catch (NullPointerException e) {
721
            String msg = 
722
                "The 'dateSysMetaLastModified' must be provided as a " + 
723
                "parameter and was not, or was an invalid representation of the timestamp.";
724
            logMetacat.error(msg);
725
            throw new InvalidRequest("1334", msg);
726
            
727
        }      
728
        final Date dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
729
        
730
        // check authorization before sending to implementation
731
        boolean authorized = MNodeService.getInstance(request).isAdminAuthorized(session);
732
        if (!authorized) {
733
        	String msg = "User is not authorized to call systemMetadataChanged";
734
            NotAuthorized failure = new NotAuthorized("1331", msg);
735
        	throw failure;
736
        }
737
        
738
        // run it in a thread to avoid connection timeout
739
        Runnable runner = new Runnable() {
740
            @Override
741
            public void run() {
742
                try {
743
                   // call the service
744
                    MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
745
                } catch (Exception e) {
746
                    logMetacat.error("Error running replication: " + e.getMessage(), e);
747
                    throw new RuntimeException(e.getMessage(), e);
748
                }
749
            }
750
        };
751
        // submit the task, and that's it
752
        executor.submit(runner);
753
        
754
        // thread was started, so we return success
755
        response.setStatus(200);
756
    }
757
    
758
    /**
759
     * Handles identifier generation calls
760
     * 
761
     * @throws InvalidRequest 
762
     * @throws NotImplemented 
763
     * @throws NotAuthorized 
764
     * @throws ServiceFailure 
765
     * @throws InvalidToken 
766
     * @throws IOException 
767
     * @throws MarshallingException 
768
     */
769
    private void generateIdentifier() throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, IOException, MarshallingException {
770
        
771
        // make sure we have the multipart params
772
        try {
773
			initMultipartParams();
774
		} catch (Exception e1) {
775
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
776
		}
777
        
778
        // get the scheme
779
		String scheme = null;
780
        try {
781
        	scheme = multipartparams.get("scheme").get(0);	           
782
        } catch (NullPointerException e) {
783
            String msg = "The 'scheme' parameter was not provided, using default";
784
            logMetacat.warn(msg);
785
        }
786
        
787
        // get the fragment
788
		String fragment = null;
789
        try {
790
        	fragment = multipartparams.get("fragment").get(0);	           
791
        } catch (NullPointerException e) {
792
            String msg = "The 'fragment' parameter was not provided, using default";
793
            logMetacat.warn(msg);
794
        }
795

    
796
        // call the service
797
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
798
        response.setStatus(200);
799
        response.setContentType("text/xml");
800
        OutputStream out = response.getOutputStream();
801
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
802
    }
803

    
804
    /**
805
     * Checks the access policy
806
     * @param id
807
     * @return
808
     * @throws ServiceFailure
809
     * @throws InvalidToken
810
     * @throws NotFound
811
     * @throws NotAuthorized
812
     * @throws NotImplemented
813
     * @throws InvalidRequest
814
     */
815
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
816
		Identifier pid = new Identifier();
817
		pid.setValue(id);
818
		Permission permission = null;
819
		try {
820
			String perm = params.get("action")[0];
821
			permission = Permission.convert(perm);
822
		} catch (Exception e) {
823
			logMetacat.warn("No permission specified");
824
		}
825
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
826
		response.setStatus(200);
827
		response.setContentType("text/xml");
828
		return result;
829
    }
830
    
831
    private void getToken() throws Exception {
832
		
833
		if (this.session != null) {
834
			String userId = this.session.getSubject().getValue();
835
			String fullName = null;
836
			try {
837
				Person person = this.session.getSubjectInfo().getPerson(0);
838
				fullName = person.getGivenName(0) + " " + person.getFamilyName();
839
			} catch (Exception e) {
840
				logMetacat.warn(e.getMessage(), e);
841
			}
842
			
843
			String token = null;
844
			token = TokenGenerator.getInstance().getJWT(userId, fullName);
845
			
846
			response.setStatus(200);
847
			response.setContentType("text/plain");
848
	        OutputStream out = response.getOutputStream();
849
	        out.write(token.getBytes(MetaCatServlet.DEFAULT_ENCODING));
850
	        out.close();
851
		} else {
852
			response.setStatus(401);
853
			response.setContentType("text/plain");
854
			OutputStream out = response.getOutputStream();
855
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
856
	        out.close();
857
		}
858
		
859
    }
860
    
861
    private void whoami() throws Exception {
862
		
863
		if (this.session != null) {
864
			Subject subject = this.session.getSubject();
865
			SubjectInfo subjectInfo = null;
866
			try {
867
				subjectInfo = this.session.getSubjectInfo();
868
			} catch (Exception e) {
869
				logMetacat.warn(e.getMessage(), e);
870
			}
871
			
872
			response.setStatus(200);
873
			response.setContentType("text/plain");
874
	        OutputStream out = response.getOutputStream();
875
	        
876
	        if (subjectInfo != null) {
877
		        TypeMarshaller.marshalTypeToOutputStream(subjectInfo, out);
878
	        } else {
879
		        TypeMarshaller.marshalTypeToOutputStream(subject, out);
880
	        }
881
	        
882
	        out.close();
883
		} else {
884
			response.setStatus(401);
885
			response.setContentType("text/plain");
886
			OutputStream out = response.getOutputStream();
887
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
888
	        out.close();
889
		}
890
		
891
    }
892
    
893
    /**
894
     * Processes failed synchronization message
895
     * @throws NotImplemented
896
     * @throws ServiceFailure
897
     * @throws NotAuthorized
898
     * @throws InvalidRequest
899
     * @throws MarshallingException
900
     * @throws IllegalAccessException 
901
     * @throws InstantiationException 
902
     * @throws IOException 
903
     */
904
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, MarshallingException, IOException, InstantiationException, IllegalAccessException {
905
    	SynchronizationFailed syncFailed = null;
906
		try {
907
			syncFailed = collectSynchronizationFailed();
908
		} catch (ParserConfigurationException e) {
909
			throw new ServiceFailure("2161", e.getMessage());
910
		} catch (SAXException e) {
911
			throw new ServiceFailure("2161", e.getMessage());
912
		}
913
		
914
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
915
    }
916

    
917
	/**
918
     * Calculate the checksum 
919
     * @throws NotImplemented
920
     * @throws MarshallingException
921
     * @throws IOException
922
     * @throws InvalidToken
923
     * @throws ServiceFailure
924
     * @throws NotAuthorized
925
     * @throws NotFound
926
     * @throws InvalidRequest
927
     */
928
    private void checksum(String pid) throws NotImplemented, MarshallingException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
929
    	String checksumAlgorithm = "MD5";
930
    	try {
931
    		checksumAlgorithm = PropertyService.getProperty("dataone.checksumAlgorithm.default");
932
        } catch(Exception e) {
933
        	logMetacat.warn("Could not lookup configured default checksum algorithm, using: " + checksumAlgorithm);
934
        }    
935
        
936
        Identifier pidid = new Identifier();
937
        pidid.setValue(pid);
938
        try {
939
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
940
        } catch(Exception e) {
941
            //do nothing.  use the default
942
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
943
        }
944
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
945
        
946
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
947
        logMetacat.debug("got checksum " + c.getValue());
948
        response.setStatus(200);
949
        logMetacat.debug("serializing response");
950
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
951
        logMetacat.debug("done serializing response.");
952
        
953
    }
954
    
955
	/**
956
     * handle the replicate action for MN
957
	 * @throws MarshallingException 
958
	 * @throws FileUploadException 
959
	 * @throws IOException 
960
	 * @throws InvalidRequest 
961
	 * @throws ServiceFailure 
962
	 * @throws UnsupportedType 
963
	 * @throws InsufficientResources 
964
	 * @throws NotAuthorized 
965
	 * @throws NotImplemented 
966
	 * @throws IllegalAccessException 
967
	 * @throws InstantiationException 
968
	 * @throws InvalidToken 
969
     */
970
    private void replicate() 
971
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException, 
972
        MarshallingException, NotImplemented, NotAuthorized, InsufficientResources, 
973
        UnsupportedType, InstantiationException, IllegalAccessException, InvalidToken {
974

    
975
        logMetacat.debug("in POST replicate()");
976
        ReadOnlyChecker checker = new ReadOnlyChecker();
977
        boolean isReadOnlyMode = checker.isReadOnly();
978
        if(isReadOnlyMode) {
979
            throw new ServiceFailure("2151", ReadOnlyChecker.DATAONEERROR);
980
        }
981
        
982
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
983
        boolean allowed = false;
984
        if (session == null) {
985
        	String msg = "No session was provided.";
986
            NotAuthorized failure = new NotAuthorized("2152", msg);
987
        	throw failure;
988
        } else {
989
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
990
        	if (!allowed) {
991
        		String msg = "User is not an admin user";
992
                NotAuthorized failure = new NotAuthorized("2152", msg);
993
            	throw failure;
994
        	}
995
        }
996
        
997
        // parse the systemMetadata
998
        Map<String, File> files = collectMultipartFiles();        
999
        final SystemMetadata sysmeta = TypeMarshaller.unmarshalTypeFromFile(SystemMetadata.class, files.get("sysmeta"));
1000
        
1001
        String sn = multipartparams.get("sourceNode").get(0);
1002
        logMetacat.debug("sourceNode: " + sn);
1003
        final NodeReference sourceNode = new NodeReference();
1004
        sourceNode.setValue(sn);
1005
        
1006
        // run it in a thread to avoid connection timeout
1007
        Runnable runner = new Runnable() {
1008
			@Override
1009
			public void run() {
1010
				try {
1011
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
1012
				} catch (Exception e) {
1013
					logMetacat.error("Error running replication: " + e.getMessage(), e);
1014
					throw new RuntimeException(e.getMessage(), e);
1015
				}
1016
			}
1017
    	};
1018
    	// submit the task, and that's it
1019
    	executor.submit(runner);
1020
        
1021
    	// thread was started, so we return success
1022
        response.setStatus(200);
1023
        
1024
    }
1025

    
1026
    /**
1027
     * Handle the getReplica action for the MN
1028
     * @param id  the identifier for the object
1029
     * @throws NotFound 
1030
     * @throws ServiceFailure 
1031
     * @throws NotImplemented 
1032
     * @throws NotAuthorized 
1033
     * @throws InvalidToken 
1034
     * @throws InvalidRequest 
1035
     */
1036
    private void getReplica(String id) 
1037
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
1038
        ServiceFailure, NotFound {
1039
        
1040
        Identifier pid = new Identifier();
1041
        pid.setValue(id);
1042
        OutputStream out = null;
1043
        InputStream dataBytes = null;
1044
                
1045
        try {
1046
            // call the service
1047
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
1048

    
1049
            response.setContentType("application/octet-stream");
1050
            response.setStatus(200);
1051
            out = response.getOutputStream();
1052
            // write the object to the output stream
1053
            IOUtils.copyLarge(dataBytes, out);
1054
            
1055
        } catch (IOException e) {
1056
            String msg = "There was an error writing the output: " + e.getMessage();
1057
            logMetacat.error(msg);
1058
            throw new ServiceFailure("2181", msg);
1059
        
1060
        }
1061

    
1062
    }
1063

    
1064
    /**
1065
     * Get the Node information
1066
     * 
1067
     * @throws MarshallingException
1068
     * @throws IOException
1069
     * @throws InvalidRequest 
1070
     * @throws ServiceFailure 
1071
     * @throws NotAuthorized 
1072
     * @throws NotImplemented 
1073
     */
1074
    private void node() 
1075
        throws MarshallingException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
1076
        
1077
        Node n = MNodeService.getInstance(request).getCapabilities();
1078
        
1079
        response.setContentType("text/xml");
1080
        response.setStatus(200);
1081
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
1082
        
1083
    }
1084
    
1085
    /**
1086
     * MN_crud.describe()
1087
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
1088
     * @param pid
1089
     * @throws InvalidRequest 
1090
     * @throws NotImplemented 
1091
     * @throws NotFound 
1092
     * @throws NotAuthorized 
1093
     * @throws ServiceFailure 
1094
     * @throws InvalidToken 
1095
     */
1096
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
1097
    {
1098
        
1099
        response.setContentType("text/xml");
1100

    
1101
        Identifier id = new Identifier();
1102
        id.setValue(pid);
1103

    
1104
        DescribeResponse dr = null;
1105
        try {
1106
        	dr = MNodeService.getInstance(request).describe(session, id);
1107
        } catch (BaseException e) {
1108
        	response.setStatus(e.getCode());
1109
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
1110
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
1111
            response.addHeader("DataONE-Exception-Description", e.getDescription());
1112
            response.addHeader("DataONE-Exception-PID", id.getValue());
1113
            return;
1114
		}
1115
        
1116
        response.setStatus(200);
1117
        
1118
        //response.addHeader("pid", pid);
1119
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
1120
        response.addHeader("Content-Length", dr.getContent_Length() + "");
1121
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
1122
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
1123
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
1124

    
1125
        
1126
    }
1127
    
1128
    /**
1129
     * get the logs based on passed params.  Available 
1130
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
1131
     * for more info
1132
     * @throws NotImplemented 
1133
     * @throws InvalidRequest 
1134
     * @throws NotAuthorized 
1135
     * @throws ServiceFailure 
1136
     * @throws InvalidToken 
1137
     * @throws IOException 
1138
     * @throws MarshallingException 
1139
     */
1140
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, MarshallingException
1141
    {
1142
            
1143
        Date fromDate = null;
1144
        Date toDate = null;
1145
        String event = null;
1146
        Integer start = null;
1147
        Integer count = null;
1148
        String pidFilter = null;
1149
        
1150
        try {
1151
        	String fromDateS = params.get("fromDate")[0];
1152
            logMetacat.debug("param fromDateS: " + fromDateS);
1153
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1154
        } catch (Exception e) {
1155
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1156
        }
1157
        try {
1158
        	String toDateS = params.get("toDate")[0];
1159
            logMetacat.debug("param toDateS: " + toDateS);
1160
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1161
        } catch (Exception e) {
1162
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1163
		}
1164
        try {
1165
        	event = params.get("event")[0];
1166
        } catch (Exception e) {
1167
        	logMetacat.warn("Could not parse event: " + e.getMessage());
1168
		}
1169
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
1170
        
1171
        try {
1172
        	start =  Integer.parseInt(params.get("start")[0]);
1173
        } catch (Exception e) {
1174
			logMetacat.warn("Could not parse start: " + e.getMessage());
1175
		}
1176
        try {
1177
        	count =  Integer.parseInt(params.get("count")[0]);
1178
        } catch (Exception e) {
1179
			logMetacat.warn("Could not parse count: " + e.getMessage());
1180
		}
1181
        
1182
        try {
1183
            pidFilter = params.get("idFilter")[0];
1184
        } catch (Exception e) {
1185
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1186
        }
1187
        
1188
        logMetacat.debug("calling getLogRecords");
1189
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1190
        
1191
        OutputStream out = response.getOutputStream();
1192
        response.setStatus(200);
1193
        response.setContentType("text/xml");
1194
        
1195
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1196
        
1197
    }
1198
    
1199
    
1200
    
1201
    /**
1202
     * Implements REST version of DataONE CRUD API --> get
1203
     * @param pid ID of data object to be read
1204
     * @throws NotImplemented 
1205
     * @throws InvalidRequest 
1206
     * @throws NotFound 
1207
     * @throws NotAuthorized 
1208
     * @throws ServiceFailure 
1209
     * @throws InvalidToken 
1210
     * @throws IOException 
1211
     * @throws MarshallingException 
1212
     */
1213
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, MarshallingException {
1214
        OutputStream out = null;
1215
        
1216
        if (pid != null) { //get a specific document                
1217
            Identifier id = new Identifier();
1218
            id.setValue(pid);
1219
                
1220
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1221
            
1222
            // set the headers for the content
1223
            String mimeType = null;
1224
            String charset = null;
1225
            ObjectFormat objectFormat = null;
1226
            
1227
            try {
1228
            	objectFormat = ObjectFormatCache.getInstance().getFormat(sm.getFormatId());
1229
        	} catch (BaseException be) {
1230
        		logMetacat.warn("Could not lookup ObjectFormat for: " + sm.getFormatId(), be);
1231
        	}
1232
            // do we have mediaType/encoding in SM?
1233
            MediaType mediaType = sm.getMediaType();
1234
            if (mediaType == null && objectFormat != null) {
1235
            	try {
1236
            		mediaType = objectFormat.getMediaType();
1237
            	} catch (Exception e) {
1238
            		logMetacat.warn("Could not lookup MediaType for: " + sm.getFormatId(), e);
1239
            	}
1240
            }
1241
            if (mediaType != null) {
1242
                mimeType = mediaType.getName();
1243
                if (mediaType.getPropertyList() != null) {
1244
                	Iterator<MediaTypeProperty> iter = mediaType.getPropertyList().iterator();
1245
                	while (iter.hasNext()) {
1246
                		MediaTypeProperty mtp = iter.next();
1247
                		if (mtp.getName().equalsIgnoreCase("charset")) {
1248
                			charset = mtp.getValue();
1249
                			mimeType += "; charset=" + charset;
1250
                			break;
1251
                		}
1252
                	}
1253
                }
1254
            }
1255
            // check object format
1256
            
1257
            // use the fallback from v1 impl
1258
            if (mimeType == null) {
1259
	            mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1260
	            
1261
	            // still null?
1262
	            if (mimeType == null) {
1263
	            	mimeType = "application/octet-stream";
1264
	            }
1265
            }
1266
            
1267
            // check for filename in SM first
1268
            String filename = sm.getFileName();
1269
            // then fallback to using id and extension
1270
            if (filename == null) {
1271
	            String extension = objectFormat.getExtension();
1272
	            if (extension == null) {
1273
	            	extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1274
	            }
1275
	            filename = id.getValue();
1276
	            if (extension != null && filename != null && !filename.endsWith(extension)) {
1277
	            	filename = id.getValue() + "." + extension;
1278
	            }
1279
            }
1280
            response.setContentType(mimeType);
1281
            response.setHeader("Content-Disposition", "inline; filename=\"" + filename+"\"");
1282
            
1283
            InputStream data = MNodeService.getInstance(request).get(session, id);
1284

    
1285
            out = response.getOutputStream();  
1286
            IOUtils.copyLarge(data, out);
1287
            
1288
        }
1289
        else
1290
        { //call listObjects with specified params
1291
            Date startTime = null;
1292
            Date endTime = null;
1293
            ObjectFormatIdentifier formatId = null;
1294
            Identifier identifier = null;
1295
            boolean replicaStatus = true;
1296
            int start = 0;
1297
            //TODO: make the max count into a const
1298
            int count = 1000;
1299
            Enumeration paramlist = request.getParameterNames();
1300
            while (paramlist.hasMoreElements()) 
1301
            { //parse the params and make the crud call
1302
                String name = (String) paramlist.nextElement();
1303
                String[] value = (String[])request.getParameterValues(name);
1304

    
1305
                if (name.equals("fromDate") && value != null)
1306
                {
1307
                    try
1308
                    {
1309
                      //startTime = dateFormat.parse(value[0]);
1310
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1311
                        //startTime = parseDateAndConvertToGMT(value[0]);
1312
                    }
1313
                    catch(Exception e)
1314
                    {  //if we can't parse it, just don't use the fromDate param
1315
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1316
                        startTime = null;
1317
                    }
1318
                }
1319
                else if(name.equals("toDate") && value != null)
1320
                {
1321
                    try
1322
                    {
1323
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1324
                    }
1325
                    catch(Exception e)
1326
                    {  //if we can't parse it, just don't use the toDate param
1327
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1328
                        endTime = null;
1329
                    }
1330
                }
1331
                else if(name.equals("formatId") && value != null) 
1332
                {
1333
                	formatId = new ObjectFormatIdentifier();
1334
                	formatId.setValue(value[0]);
1335
                }
1336
                else if(name.equals("identifier") && value != null) 
1337
                {
1338
                	identifier = new Identifier();
1339
                	identifier.setValue(value[0]);
1340
                }
1341
                else if(name.equals("replicaStatus") && value != null)
1342
                {
1343
                    if(value != null && 
1344
                       value.length > 0 && 
1345
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1346
                    {
1347
                        replicaStatus = false;
1348
                    }
1349
                }
1350
                else if(name.equals("start") && value != null)
1351
                {
1352
                    start = new Integer(value[0]).intValue();
1353
                }
1354
                else if(name.equals("count") && value != null)
1355
                {
1356
                    count = new Integer(value[0]).intValue();
1357
                }
1358
            }
1359
            //make the crud call
1360
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1361
                    " endTime: " + endTime + " formatId: " + 
1362
                    formatId + " replicaStatus: " + replicaStatus + 
1363
                    " start: " + start + " count: " + count);
1364
           
1365
            ObjectList ol = 
1366
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1367
           			formatId, identifier, replicaStatus, start, count);
1368
           
1369
            out = response.getOutputStream();  
1370
            response.setStatus(200);
1371
            response.setContentType("text/xml");
1372
            // Serialize and write it to the output stream
1373
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1374
            
1375
        }
1376
        
1377
    }
1378
    
1379

    
1380
    /**
1381
     * Retrieve data package as Bagit zip
1382
     * @param pid
1383
     * @throws NotImplemented 
1384
     * @throws NotFound 
1385
     * @throws NotAuthorized 
1386
     * @throws ServiceFailure 
1387
     * @throws InvalidToken 
1388
     * @throws IOException 
1389
     * @throws InvalidRequest 
1390
     */
1391
    protected void getPackage(String format, String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1392

    
1393
        Identifier id = new Identifier();
1394
        id.setValue(pid);
1395
        ObjectFormatIdentifier formatId = null;
1396
        if (format != null) {
1397
        	formatId = new ObjectFormatIdentifier();
1398
        	formatId.setValue(format);
1399
        }
1400
		InputStream is = MNodeService.getInstance(request).getPackage(session, formatId , id);
1401
        
1402
        // use the provided filename
1403
        String filename = null;
1404
        if (is instanceof DeleteOnCloseFileInputStream) {
1405
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1406
        } else {
1407
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1408
        }
1409
        response.setHeader("Content-Disposition", "inline; filename=\"" + filename+"\"");
1410
        response.setContentType("application/zip");
1411
        response.setStatus(200);
1412
        OutputStream out = response.getOutputStream();
1413
        
1414
        // write it to the output stream
1415
        IOUtils.copyLarge(is, out);
1416
   }
1417
    
1418
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1419
			NotAuthorized, NotFound, NotImplemented, IOException,
1420
			MarshallingException, InvalidRequest, IdentifierNotUnique,
1421
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1422

    
1423
		// publish the object
1424
		Identifier originalIdentifier = new Identifier();
1425
		originalIdentifier.setValue(pid);
1426
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1427

    
1428
		response.setStatus(200);
1429
		response.setContentType("text/xml");
1430
		OutputStream out = response.getOutputStream();
1431

    
1432
		// write new identifier to the output stream
1433
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1434
	}
1435
    
1436
    /**
1437
     * Retrieve System Metadata
1438
     * @param pid
1439
     * @throws InvalidToken
1440
     * @throws ServiceFailure
1441
     * @throws NotAuthorized
1442
     * @throws NotFound
1443
     * @throws InvalidRequest
1444
     * @throws NotImplemented
1445
     * @throws IOException
1446
     * @throws MarshallingException
1447
     */
1448
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, MarshallingException {
1449

    
1450
        Identifier id = new Identifier();
1451
        id.setValue(pid);
1452
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1453
        
1454
        response.setContentType("text/xml");
1455
        response.setStatus(200);
1456
        OutputStream out = response.getOutputStream();
1457
        
1458
        // Serialize and write it to the output stream
1459
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1460
   }
1461
    
1462
    
1463
    /**
1464
     * Inserts or updates the object
1465
     * 
1466
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1467
     *               is the existing pid.  If insert, the pid is the new one
1468
     * @throws InvalidRequest 
1469
     * @throws ServiceFailure 
1470
     * @throws MarshallingException 
1471
     * @throws NotImplemented 
1472
     * @throws InvalidSystemMetadata 
1473
     * @throws InsufficientResources 
1474
     * @throws UnsupportedType 
1475
     * @throws IdentifierNotUnique 
1476
     * @throws NotAuthorized 
1477
     * @throws InvalidToken 
1478
     * @throws NotFound 
1479
     * @throws IOException 
1480
     * @throws IllegalAccessException 
1481
     * @throws InstantiationException 
1482
     */
1483
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, MarshallingException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1484
       
1485
    	// Read the incoming data from its Mime Multipart encoding
1486
    	Map<String, File> files = collectMultipartFiles();
1487
               
1488
    	Identifier pid = new Identifier();
1489
        if (trailingPid == null) {
1490
	        // get the pid string from the body and set the value
1491
	        String pidString = multipartparams.get("pid").get(0);
1492
	        if (pidString != null) {
1493
            pid.setValue(pidString);
1494
            
1495
          } else {
1496
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1497
              
1498
          }
1499
        } else {
1500
        	// use the pid included in the URL
1501
        	pid.setValue(trailingPid);
1502
        }
1503
        logMetacat.debug("putObject with pid " + pid.getValue());
1504
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1505

    
1506
        InputStream object = null;
1507
        InputStream sysmeta = null;
1508
        File smFile = files.get("sysmeta");
1509
        File objFile = files.get("object");
1510
        // ensure we have the object bytes
1511
        if  ( objFile == null ) {
1512
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1513
            
1514
        }
1515
        object = new FileInputStream(objFile);
1516
        
1517
        // ensure we have the system metadata
1518
        if  ( smFile == null ) {
1519
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1520
            
1521
        }
1522
        sysmeta = new FileInputStream(smFile);
1523
        
1524
        response.setStatus(200);
1525
        response.setContentType("text/xml");
1526
        OutputStream out = response.getOutputStream();
1527
        
1528
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1529
            // handle inserts
1530
            logMetacat.debug("Commence creation...");
1531
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1532

    
1533
            logMetacat.debug("creating object with pid " + pid.getValue());
1534
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1535
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1536
            
1537
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1538
        	// handle updates
1539
        	
1540
            // construct pids
1541
            Identifier newPid = null;
1542
            try {
1543
            	String newPidString = multipartparams.get("newPid").get(0);
1544
            	newPid = new Identifier();
1545
            	newPid.setValue(newPidString);
1546
            } catch (Exception e) {
1547
				logMetacat.error("Could not get newPid from request");
1548
			}
1549
            logMetacat.debug("Commence update...");
1550
            
1551
            // get the systemmetadata object
1552
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1553

    
1554
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1555
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1556
        } else {
1557
            throw new InvalidRequest("1000", "Operation must be create or update.");
1558
        }
1559
   
1560
    }
1561

    
1562
    /**
1563
     * Handle delete 
1564
     * @param pid ID of data object to be deleted
1565
     * @throws IOException
1566
     * @throws InvalidRequest 
1567
     * @throws NotImplemented 
1568
     * @throws NotFound 
1569
     * @throws NotAuthorized 
1570
     * @throws ServiceFailure 
1571
     * @throws InvalidToken 
1572
     * @throws MarshallingException 
1573
     */
1574
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, MarshallingException 
1575
    {
1576

    
1577
        OutputStream out = response.getOutputStream();
1578
        response.setStatus(200);
1579
        response.setContentType("text/xml");
1580

    
1581
        Identifier id = new Identifier();
1582
        id.setValue(pid);
1583

    
1584
        logMetacat.debug("Calling delete");
1585
        MNodeService.getInstance(request).delete(session, id);
1586
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1587
        
1588
    }
1589
    
1590
    /**
1591
     * Archives the given pid
1592
     * @param pid
1593
     * @throws InvalidToken
1594
     * @throws ServiceFailure
1595
     * @throws NotAuthorized
1596
     * @throws NotFound
1597
     * @throws NotImplemented
1598
     * @throws IOException
1599
     * @throws MarshallingException
1600
     */
1601
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, MarshallingException {
1602

    
1603
        OutputStream out = response.getOutputStream();
1604
        response.setStatus(200);
1605
        response.setContentType("text/xml");
1606

    
1607
        Identifier id = new Identifier();
1608
        id.setValue(pid);
1609

    
1610
        logMetacat.debug("Calling archive");
1611
        MNodeService.getInstance(request).archive(session, id);
1612
        
1613
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1614
        
1615
    }
1616

    
1617
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, MarshallingException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1618
		
1619
		// Read the incoming data from its Mime Multipart encoding
1620
		logMetacat.debug("Disassembling MIME multipart form");
1621
		InputStream sf = null;
1622
	
1623
		// handle MMP inputs
1624
		File tmpDir = getTempDirectory();
1625
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1626
		MultipartRequestResolver mrr = 
1627
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), MAX_UPLOAD_SIZE, 0);
1628
		MultipartRequest mr = null;
1629
		try {
1630
			mr = mrr.resolveMultipart(request);
1631
		} catch (Exception e) {
1632
			throw new ServiceFailure("2161", 
1633
					"Could not resolve multipart: " + e.getMessage());
1634
		}
1635
		logMetacat.debug("resolved multipart request");
1636
		Map<String, File> files = mr.getMultipartFiles();
1637
		if (files == null || files.keySet() == null) {
1638
			throw new InvalidRequest("2163",
1639
					"must have multipart file with name 'message'");
1640
		}
1641
		logMetacat.debug("got multipart files");
1642
	
1643
		multipartparams = mr.getMultipartParameters();
1644
	
1645
		File sfFile = files.get("message");
1646
		if (sfFile == null) {
1647
			throw new InvalidRequest("2163",
1648
					"Missing the required file-part 'message' from the multipart request.");
1649
		}
1650
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1651
		sf = new FileInputStream(sfFile);
1652
	
1653
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1654
		return syncFailed;
1655
	}
1656
	
1657
	/**
1658
	 * Update the system metadata for a specified identifier
1659
	 * @throws ServiceFailure
1660
	 * @throws InvalidRequest
1661
	 * @throws InstantiationException
1662
	 * @throws IllegalAccessException
1663
	 * @throws IOException
1664
	 * @throws MarshallingException
1665
	 * @throws NotImplemented
1666
	 * @throws NotAuthorized
1667
	 * @throws InvalidSystemMetadata
1668
	 * @throws InvalidToken
1669
	 */
1670
	protected void updateSystemMetadata() throws ServiceFailure, InvalidRequest, 
1671
	                        InstantiationException, IllegalAccessException, IOException, MarshallingException, NotImplemented, 
1672
	                        NotAuthorized, InvalidSystemMetadata, InvalidToken {
1673
	    // Read the incoming data from its Mime Multipart encoding
1674
        Map<String, File> files = collectMultipartFiles();
1675
        
1676
        // get the encoded pid string from the body and make the object
1677
        String pidString = multipartparams.get("pid").get(0);
1678
        Identifier pid = new Identifier();
1679
        pid.setValue(pidString);
1680
        
1681
        logMetacat.debug("updateSystemMetadata: " + pid);
1682

    
1683
        // get the system metadata from the request
1684
        File smFile = files.get("sysmeta");
1685
        FileInputStream sysmeta = new FileInputStream(smFile);
1686
        SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1687

    
1688
        logMetacat.debug("updating system metadata with pid " + pid.getValue());
1689
        
1690
        MNodeService.getInstance(request).updateSystemMetadata(session, pid, systemMetadata);
1691
	}
1692

    
1693
}
(3-3/4)