Project

General

Profile

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

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

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

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

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

    
153

    
154

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
617
    			// set the content-type if we have it from the implementation
618
    			if (stream instanceof ContentTypeInputStream) {
619
    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
620
    			}
621
                response.setStatus(200);
622
                out = response.getOutputStream();
623
                // write the results to the output stream
624
                IOUtils.copyLarge(stream, out);
625
                return;
626
    		} else {
627
    			// TODO: list the registered views
628
                //BaseException ni = new NotImplemented("9999", "MN.listViews() is not implemented at this node");
629
				//throw ni;
630
    		    OptionList list = mnode.listViews(session);
631
    	        
632
    	        response.setContentType("text/xml");
633
    	        response.setStatus(200);
634
    	        TypeMarshaller.marshalTypeToOutputStream(list, response.getOutputStream());
635
    		}
636
	    	
637
	        
638
    	} catch (BaseException be) {
639
        	// report Exceptions as clearly as possible
640
			try {
641
				out = response.getOutputStream();
642
			} catch (IOException e) {
643
				logMetacat.error("Could not get output stream from response", e);
644
			}
645
            serializeException(be, out);
646
        } catch (Exception e) {
647
            // report Exceptions as clearly and generically as possible
648
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
649
			try {
650
				out = response.getOutputStream();
651
			} catch (IOException ioe) {
652
				logMetacat.error("Could not get output stream from response", ioe);
653
			}
654
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
655
            serializeException(se, out);
656
        }
657
    }
658
    
659
    /**
660
     * Handles notification of system metadata changes for the given identifier
661
     * 
662
     * @param id  the identifier for the object
663
     * @throws InvalidToken 
664
     * @throws InvalidRequest 
665
     * @throws NotAuthorized 
666
     * @throws ServiceFailure 
667
     * @throws NotImplemented 
668
     */
669
    private void systemMetadataChanged() 
670
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
671
        InvalidToken {
672

    
673
        //final long serialVersion = 0L;
674
        String serialVersionStr = null;
675
        String dateSysMetaLastModifiedStr = null;
676
        
677
        // mkae sure we have the multipart params
678
        try {
679
			initMultipartParams();
680
		} catch (Exception e1) {
681
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
682
		}
683
        
684
        // get the pid
685
        String id = null;
686
        try {
687
        	id = multipartparams.get("pid").get(0);
688
        } catch (NullPointerException e) {
689
            String msg = "The 'pid' must be provided as a parameter and was not.";
690
            logMetacat.error(msg);
691
            throw new InvalidRequest("1334", msg);
692
        }  
693
        final Identifier pid = new Identifier();
694
        pid.setValue(id);
695
        
696
        // get the serialVersion
697
        try {
698
            serialVersionStr = multipartparams.get("serialVersion").get(0);
699
        } catch (NullPointerException e) {
700
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
701
            logMetacat.error(msg);
702
            throw new InvalidRequest("1334", msg);
703
            
704
        }  
705
        
706
        final long serialVersion = (new Long(serialVersionStr)).longValue();
707
        
708
        // get the dateSysMetaLastModified
709
        try {
710
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
711
            
712
            
713
        } catch (NullPointerException e) {
714
            String msg = 
715
                "The 'dateSysMetaLastModified' must be provided as a " + 
716
                "parameter and was not, or was an invalid representation of the timestamp.";
717
            logMetacat.error(msg);
718
            throw new InvalidRequest("1334", msg);
719
            
720
        }      
721
        final Date dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
722
        
723
        // run it in a thread to avoid connection timeout
724
        Runnable runner = new Runnable() {
725
            @Override
726
            public void run() {
727
                try {
728
                   // call the service
729
                    MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
730
                } catch (Exception e) {
731
                    logMetacat.error("Error running replication: " + e.getMessage(), e);
732
                    throw new RuntimeException(e.getMessage(), e);
733
                }
734
            }
735
        };
736
        // submit the task, and that's it
737
        executor.submit(runner);
738
        
739
        // thread was started, so we return success
740
        response.setStatus(200);
741
    }
742
    
743
    /**
744
     * Handles identifier generation calls
745
     * 
746
     * @throws InvalidRequest 
747
     * @throws NotImplemented 
748
     * @throws NotAuthorized 
749
     * @throws ServiceFailure 
750
     * @throws InvalidToken 
751
     * @throws IOException 
752
     * @throws JiBXException 
753
     */
754
    private void generateIdentifier() throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, IOException, JiBXException {
755
        
756
        // make sure we have the multipart params
757
        try {
758
			initMultipartParams();
759
		} catch (Exception e1) {
760
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
761
		}
762
        
763
        // get the scheme
764
		String scheme = null;
765
        try {
766
        	scheme = multipartparams.get("scheme").get(0);	           
767
        } catch (NullPointerException e) {
768
            String msg = "The 'scheme' parameter was not provided, using default";
769
            logMetacat.warn(msg);
770
        }
771
        
772
        // get the fragment
773
		String fragment = null;
774
        try {
775
        	fragment = multipartparams.get("fragment").get(0);	           
776
        } catch (NullPointerException e) {
777
            String msg = "The 'fragment' parameter was not provided, using default";
778
            logMetacat.warn(msg);
779
        }
780

    
781
        // call the service
782
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
783
        response.setStatus(200);
784
        response.setContentType("text/xml");
785
        OutputStream out = response.getOutputStream();
786
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
787
    }
788

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

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

    
960
        logMetacat.debug("in POST replicate()");
961
        
962
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
963
        boolean allowed = false;
964
        if (session == null) {
965
        	String msg = "No session was provided.";
966
            NotAuthorized failure = new NotAuthorized("2152", msg);
967
        	throw failure;
968
        } else {
969
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
970
        	if (!allowed) {
971
        		String msg = "User is not an admin user";
972
                NotAuthorized failure = new NotAuthorized("2152", msg);
973
            	throw failure;
974
        	}
975
        }
976
        
977
        // parse the systemMetadata
978
        Map<String, File> files = collectMultipartFiles();        
979
        final SystemMetadata sysmeta = TypeMarshaller.unmarshalTypeFromFile(SystemMetadata.class, files.get("sysmeta"));
980
        
981
        String sn = multipartparams.get("sourceNode").get(0);
982
        logMetacat.debug("sourceNode: " + sn);
983
        final NodeReference sourceNode = new NodeReference();
984
        sourceNode.setValue(sn);
985
        
986
        // run it in a thread to avoid connection timeout
987
        Runnable runner = new Runnable() {
988
			@Override
989
			public void run() {
990
				try {
991
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
992
				} catch (Exception e) {
993
					logMetacat.error("Error running replication: " + e.getMessage(), e);
994
					throw new RuntimeException(e.getMessage(), e);
995
				}
996
			}
997
    	};
998
    	// submit the task, and that's it
999
    	executor.submit(runner);
1000
        
1001
    	// thread was started, so we return success
1002
        response.setStatus(200);
1003
        
1004
    }
1005

    
1006
    /**
1007
     * Handle the getReplica action for the MN
1008
     * @param id  the identifier for the object
1009
     * @throws NotFound 
1010
     * @throws ServiceFailure 
1011
     * @throws NotImplemented 
1012
     * @throws NotAuthorized 
1013
     * @throws InvalidToken 
1014
     * @throws InvalidRequest 
1015
     */
1016
    private void getReplica(String id) 
1017
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
1018
        ServiceFailure, NotFound {
1019
        
1020
        Identifier pid = new Identifier();
1021
        pid.setValue(id);
1022
        OutputStream out = null;
1023
        InputStream dataBytes = null;
1024
                
1025
        try {
1026
            // call the service
1027
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
1028

    
1029
            response.setContentType("application/octet-stream");
1030
            response.setStatus(200);
1031
            out = response.getOutputStream();
1032
            // write the object to the output stream
1033
            IOUtils.copyLarge(dataBytes, out);
1034
            
1035
        } catch (IOException e) {
1036
            String msg = "There was an error writing the output: " + e.getMessage();
1037
            logMetacat.error(msg);
1038
            throw new ServiceFailure("2181", msg);
1039
        
1040
        }
1041

    
1042
    }
1043

    
1044
    /**
1045
     * Get the Node information
1046
     * 
1047
     * @throws JiBXException
1048
     * @throws IOException
1049
     * @throws InvalidRequest 
1050
     * @throws ServiceFailure 
1051
     * @throws NotAuthorized 
1052
     * @throws NotImplemented 
1053
     */
1054
    private void node() 
1055
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
1056
        
1057
        Node n = MNodeService.getInstance(request).getCapabilities();
1058
        
1059
        response.setContentType("text/xml");
1060
        response.setStatus(200);
1061
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
1062
        
1063
    }
1064
    
1065
    /**
1066
     * MN_crud.describe()
1067
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
1068
     * @param pid
1069
     * @throws InvalidRequest 
1070
     * @throws NotImplemented 
1071
     * @throws NotFound 
1072
     * @throws NotAuthorized 
1073
     * @throws ServiceFailure 
1074
     * @throws InvalidToken 
1075
     */
1076
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
1077
    {
1078
        
1079
        response.setContentType("text/xml");
1080

    
1081
        Identifier id = new Identifier();
1082
        id.setValue(pid);
1083

    
1084
        DescribeResponse dr = null;
1085
        try {
1086
        	dr = MNodeService.getInstance(request).describe(session, id);
1087
        } catch (BaseException e) {
1088
        	response.setStatus(e.getCode());
1089
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
1090
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
1091
            response.addHeader("DataONE-Exception-Description", e.getDescription());
1092
            response.addHeader("DataONE-Exception-PID", id.getValue());
1093
            return;
1094
		}
1095
        
1096
        response.setStatus(200);
1097
        
1098
        //response.addHeader("pid", pid);
1099
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
1100
        response.addHeader("Content-Length", dr.getContent_Length() + "");
1101
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
1102
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
1103
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
1104

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

    
1265
            out = response.getOutputStream();  
1266
            IOUtils.copyLarge(data, out);
1267
            
1268
        }
1269
        else
1270
        { //call listObjects with specified params
1271
            Date startTime = null;
1272
            Date endTime = null;
1273
            ObjectFormatIdentifier formatId = null;
1274
            Identifier identifier = null;
1275
            boolean replicaStatus = true;
1276
            int start = 0;
1277
            //TODO: make the max count into a const
1278
            int count = 1000;
1279
            Enumeration paramlist = request.getParameterNames();
1280
            while (paramlist.hasMoreElements()) 
1281
            { //parse the params and make the crud call
1282
                String name = (String) paramlist.nextElement();
1283
                String[] value = (String[])request.getParameterValues(name);
1284

    
1285
                if (name.equals("fromDate") && value != null)
1286
                {
1287
                    try
1288
                    {
1289
                      //startTime = dateFormat.parse(value[0]);
1290
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1291
                        //startTime = parseDateAndConvertToGMT(value[0]);
1292
                    }
1293
                    catch(Exception e)
1294
                    {  //if we can't parse it, just don't use the fromDate param
1295
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1296
                        startTime = null;
1297
                    }
1298
                }
1299
                else if(name.equals("toDate") && value != null)
1300
                {
1301
                    try
1302
                    {
1303
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1304
                    }
1305
                    catch(Exception e)
1306
                    {  //if we can't parse it, just don't use the toDate param
1307
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1308
                        endTime = null;
1309
                    }
1310
                }
1311
                else if(name.equals("formatId") && value != null) 
1312
                {
1313
                	formatId = new ObjectFormatIdentifier();
1314
                	formatId.setValue(value[0]);
1315
                }
1316
                else if(name.equals("identifier") && value != null) 
1317
                {
1318
                	identifier = new Identifier();
1319
                	identifier.setValue(value[0]);
1320
                }
1321
                else if(name.equals("replicaStatus") && value != null)
1322
                {
1323
                    if(value != null && 
1324
                       value.length > 0 && 
1325
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1326
                    {
1327
                        replicaStatus = false;
1328
                    }
1329
                }
1330
                else if(name.equals("start") && value != null)
1331
                {
1332
                    start = new Integer(value[0]).intValue();
1333
                }
1334
                else if(name.equals("count") && value != null)
1335
                {
1336
                    count = new Integer(value[0]).intValue();
1337
                }
1338
            }
1339
            //make the crud call
1340
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1341
                    " endTime: " + endTime + " formatId: " + 
1342
                    formatId + " replicaStatus: " + replicaStatus + 
1343
                    " start: " + start + " count: " + count);
1344
           
1345
            ObjectList ol = 
1346
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
1347
           			formatId, identifier, replicaStatus, start, count);
1348
           
1349
            out = response.getOutputStream();  
1350
            response.setStatus(200);
1351
            response.setContentType("text/xml");
1352
            // Serialize and write it to the output stream
1353
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1354
            
1355
        }
1356
        
1357
    }
1358
    
1359

    
1360
    /**
1361
     * Retrieve data package as Bagit zip
1362
     * @param pid
1363
     * @throws NotImplemented 
1364
     * @throws NotFound 
1365
     * @throws NotAuthorized 
1366
     * @throws ServiceFailure 
1367
     * @throws InvalidToken 
1368
     * @throws IOException 
1369
     * @throws InvalidRequest 
1370
     */
1371
    protected void getPackage(String format, String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1372

    
1373
        Identifier id = new Identifier();
1374
        id.setValue(pid);
1375
        ObjectFormatIdentifier formatId = null;
1376
        if (format != null) {
1377
        	formatId = new ObjectFormatIdentifier();
1378
        	formatId.setValue(format);
1379
        }
1380
		InputStream is = MNodeService.getInstance(request).getPackage(session, formatId , id);
1381
        
1382
        // use the provided filename
1383
        String filename = null;
1384
        if (is instanceof DeleteOnCloseFileInputStream) {
1385
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1386
        } else {
1387
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1388
        }
1389
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1390
        response.setContentType("application/zip");
1391
        response.setStatus(200);
1392
        OutputStream out = response.getOutputStream();
1393
        
1394
        // write it to the output stream
1395
        IOUtils.copyLarge(is, out);
1396
   }
1397
    
1398
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1399
			NotAuthorized, NotFound, NotImplemented, IOException,
1400
			JiBXException, InvalidRequest, IdentifierNotUnique,
1401
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1402

    
1403
		// publish the object
1404
		Identifier originalIdentifier = new Identifier();
1405
		originalIdentifier.setValue(pid);
1406
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1407

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

    
1412
		// write new identifier to the output stream
1413
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1414
	}
1415
    
1416
    /**
1417
     * Retrieve System Metadata
1418
     * @param pid
1419
     * @throws InvalidToken
1420
     * @throws ServiceFailure
1421
     * @throws NotAuthorized
1422
     * @throws NotFound
1423
     * @throws InvalidRequest
1424
     * @throws NotImplemented
1425
     * @throws IOException
1426
     * @throws JiBXException
1427
     */
1428
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1429

    
1430
        Identifier id = new Identifier();
1431
        id.setValue(pid);
1432
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1433
        
1434
        response.setContentType("text/xml");
1435
        response.setStatus(200);
1436
        OutputStream out = response.getOutputStream();
1437
        
1438
        // Serialize and write it to the output stream
1439
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1440
   }
1441
    
1442
    
1443
    /**
1444
     * Inserts or updates the object
1445
     * 
1446
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1447
     *               is the existing pid.  If insert, the pid is the new one
1448
     * @throws InvalidRequest 
1449
     * @throws ServiceFailure 
1450
     * @throws JiBXException 
1451
     * @throws NotImplemented 
1452
     * @throws InvalidSystemMetadata 
1453
     * @throws InsufficientResources 
1454
     * @throws UnsupportedType 
1455
     * @throws IdentifierNotUnique 
1456
     * @throws NotAuthorized 
1457
     * @throws InvalidToken 
1458
     * @throws NotFound 
1459
     * @throws IOException 
1460
     * @throws IllegalAccessException 
1461
     * @throws InstantiationException 
1462
     */
1463
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1464
       
1465
    	// Read the incoming data from its Mime Multipart encoding
1466
    	Map<String, File> files = collectMultipartFiles();
1467
               
1468
    	Identifier pid = new Identifier();
1469
        if (trailingPid == null) {
1470
	        // get the pid string from the body and set the value
1471
	        String pidString = multipartparams.get("pid").get(0);
1472
	        if (pidString != null) {
1473
            pid.setValue(pidString);
1474
            
1475
          } else {
1476
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1477
              
1478
          }
1479
        } else {
1480
        	// use the pid included in the URL
1481
        	pid.setValue(trailingPid);
1482
        }
1483
        logMetacat.debug("putObject with pid " + pid.getValue());
1484
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1485

    
1486
        InputStream object = null;
1487
        InputStream sysmeta = null;
1488
        File smFile = files.get("sysmeta");
1489
        sysmeta = new FileInputStream(smFile);
1490
        File objFile = files.get("object");
1491
        object = new FileInputStream(objFile);
1492
        
1493
        // ensure we have the object bytes
1494
        if  ( objFile == null ) {
1495
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1496
            
1497
        }
1498
        
1499
        // ensure we have the system metadata
1500
        if  ( smFile == null ) {
1501
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1502
            
1503
        }
1504
        
1505
        response.setStatus(200);
1506
        response.setContentType("text/xml");
1507
        OutputStream out = response.getOutputStream();
1508
        
1509
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1510
            // handle inserts
1511
            logMetacat.debug("Commence creation...");
1512
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1513

    
1514
            logMetacat.debug("creating object with pid " + pid.getValue());
1515
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1516
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1517
            
1518
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1519
        	// handle updates
1520
        	
1521
            // construct pids
1522
            Identifier newPid = null;
1523
            try {
1524
            	String newPidString = multipartparams.get("newPid").get(0);
1525
            	newPid = new Identifier();
1526
            	newPid.setValue(newPidString);
1527
            } catch (Exception e) {
1528
				logMetacat.error("Could not get newPid from request");
1529
			}
1530
            logMetacat.debug("Commence update...");
1531
            
1532
            // get the systemmetadata object
1533
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1534

    
1535
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1536
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1537
        } else {
1538
            throw new InvalidRequest("1000", "Operation must be create or update.");
1539
        }
1540
   
1541
    }
1542

    
1543
    /**
1544
     * Handle delete 
1545
     * @param pid ID of data object to be deleted
1546
     * @throws IOException
1547
     * @throws InvalidRequest 
1548
     * @throws NotImplemented 
1549
     * @throws NotFound 
1550
     * @throws NotAuthorized 
1551
     * @throws ServiceFailure 
1552
     * @throws InvalidToken 
1553
     * @throws JiBXException 
1554
     */
1555
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1556
    {
1557

    
1558
        OutputStream out = response.getOutputStream();
1559
        response.setStatus(200);
1560
        response.setContentType("text/xml");
1561

    
1562
        Identifier id = new Identifier();
1563
        id.setValue(pid);
1564

    
1565
        logMetacat.debug("Calling delete");
1566
        MNodeService.getInstance(request).delete(session, id);
1567
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1568
        
1569
    }
1570
    
1571
    /**
1572
     * Archives the given pid
1573
     * @param pid
1574
     * @throws InvalidToken
1575
     * @throws ServiceFailure
1576
     * @throws NotAuthorized
1577
     * @throws NotFound
1578
     * @throws NotImplemented
1579
     * @throws IOException
1580
     * @throws JiBXException
1581
     */
1582
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1583

    
1584
        OutputStream out = response.getOutputStream();
1585
        response.setStatus(200);
1586
        response.setContentType("text/xml");
1587

    
1588
        Identifier id = new Identifier();
1589
        id.setValue(pid);
1590

    
1591
        logMetacat.debug("Calling archive");
1592
        MNodeService.getInstance(request).archive(session, id);
1593
        
1594
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1595
        
1596
    }
1597

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

    
1664
        // get the system metadata from the request
1665
        File smFile = files.get("sysmeta");
1666
        FileInputStream sysmeta = new FileInputStream(smFile);
1667
        SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1668

    
1669
        logMetacat.debug("updating system metadata with pid " + pid.getValue());
1670
        
1671
        MNodeService.getInstance(request).updateSystemMetadata(session, pid, systemMetadata);
1672
	}
1673

    
1674
}
(3-3/4)