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: 2015-03-09 18:29:27 -0700 (Mon, 09 Mar 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.Map;
34
import java.util.concurrent.ExecutorService;
35
import java.util.concurrent.Executors;
36

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

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

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

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

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

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

    
145

    
146

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

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

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

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

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

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

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

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

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

    
428
	                }
429
                    
430
                    // get
431
                    if (httpVerb == GET) {
432
                        
433
                        getPackage(format, pid);
434
                        status = true;
435
                    }  
436
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
437
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
438
	                // after the command
439
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
440
	                logMetacat.debug("view extra: " + extra);
441

    
442
	                String format = null;
443
	                String pid = null;
444

    
445
	                if (extra != null) {
446
		                // get the format
447
		                int formatIndex = extra.length();
448
		                if (extra.indexOf("/") > -1) {
449
		                	formatIndex = extra.indexOf("/");
450
		                }
451
		                format = extra.substring(0, formatIndex);
452
		                logMetacat.debug("view format: " + format);
453
		                
454
		                // get the pid if it is there
455
		                pid = extra.substring(formatIndex, extra.length());
456
		                if (pid != null && pid.length() == 0) {
457
		                	pid = null;
458
		                } else {
459
		                	if (pid.startsWith("/")) {
460
		                		pid = pid.substring(1);
461
		                    }
462
		                }
463
		                logMetacat.debug("pid: " + pid);
464

    
465
	                }
466
	                logMetacat.debug("verb:" + httpVerb);
467
	                if (httpVerb == GET) {
468
	                    doViews(format, pid);
469
	                    status = true;
470
	                }
471
                } 
472
                
473
                if (!status) {
474
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
475
                }
476
            } else {
477
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
478
            }
479
        } catch (BaseException be) {
480
        	// report Exceptions as clearly as possible
481
        	OutputStream out = null;
482
			try {
483
				out = response.getOutputStream();
484
			} catch (IOException e) {
485
				logMetacat.error("Could not get output stream from response", e);
486
			}
487
            serializeException(be, out);
488
        } catch (Exception e) {
489
            // report Exceptions as clearly and generically as possible
490
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
491
        	OutputStream out = null;
492
			try {
493
				out = response.getOutputStream();
494
			} catch (IOException ioe) {
495
				logMetacat.error("Could not get output stream from response", ioe);
496
			}
497
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
498
            serializeException(se, out);
499
        }
500
    }
501
    
502

    
503
    private void doQuery(String engine, String query) {
504
    	
505
		OutputStream out = null;
506

    
507
    	try {
508
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter			
509
	    	if (engine == null) {
510
	    		// just looking for list of engines
511
	    		MNodeService mnode = MNodeService.getInstance(request);
512
    			mnode.setSession(session);
513
	    		QueryEngineList qel = mnode.listQueryEngines(session);
514
	    		response.setContentType("text/xml");
515
	            response.setStatus(200);
516
	            out = response.getOutputStream();
517
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
518
	            return;
519
	    	} else {
520
	    		if (query != null) {
521
	    			MNodeService mnode = MNodeService.getInstance(request);
522
	    			mnode.setSession(session);
523
	    			InputStream stream = mnode.query(session, engine, query);
524

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

    
575
    	try {
576
    		// get a list of views
577
    		if (pid != null) {
578
    			Identifier identifier = new Identifier();
579
    			identifier.setValue(pid);
580
				InputStream stream = mnode.view(session, format, identifier);
581

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

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

    
731
        // call the service
732
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
733
        response.setStatus(200);
734
        response.setContentType("text/xml");
735
        OutputStream out = response.getOutputStream();
736
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
737
    }
738

    
739
    /**
740
     * Checks the access policy
741
     * @param id
742
     * @return
743
     * @throws ServiceFailure
744
     * @throws InvalidToken
745
     * @throws NotFound
746
     * @throws NotAuthorized
747
     * @throws NotImplemented
748
     * @throws InvalidRequest
749
     */
750
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
751
		Identifier pid = new Identifier();
752
		pid.setValue(id);
753
		Permission permission = null;
754
		try {
755
			String perm = params.get("action")[0];
756
			permission = Permission.convert(perm);
757
		} catch (Exception e) {
758
			logMetacat.warn("No permission specified");
759
		}
760
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
761
		response.setStatus(200);
762
		response.setContentType("text/xml");
763
		return result;
764
    }
765
    
766
    private void getToken() throws Exception {
767
		
768
		if (this.session != null) {
769
			String userId = this.session.getSubject().getValue();
770
			String fullName = null;
771
			try {
772
				Person person = this.session.getSubjectInfo().getPerson(0);
773
				fullName = person.getGivenName(0) + " " + person.getFamilyName();
774
			} catch (Exception e) {
775
				logMetacat.warn(e.getMessage(), e);
776
			}
777
			
778
			String token = null;
779
			token = TokenGenerator.getJWT(userId, fullName);
780
			
781
			response.setStatus(200);
782
			response.setContentType("text/plain");
783
	        OutputStream out = response.getOutputStream();
784
	        out.write(token.getBytes(MetaCatServlet.DEFAULT_ENCODING));
785
	        out.close();
786
		} else {
787
			response.setStatus(401);
788
			response.setContentType("text/plain");
789
			OutputStream out = response.getOutputStream();
790
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
791
	        out.close();
792
		}
793
		
794
    }
795
    
796
    /**
797
     * Processes failed synchronization message
798
     * @throws NotImplemented
799
     * @throws ServiceFailure
800
     * @throws NotAuthorized
801
     * @throws InvalidRequest
802
     * @throws JiBXException
803
     * @throws IllegalAccessException 
804
     * @throws InstantiationException 
805
     * @throws IOException 
806
     */
807
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
808
    	SynchronizationFailed syncFailed = null;
809
		try {
810
			syncFailed = collectSynchronizationFailed();
811
		} catch (ParserConfigurationException e) {
812
			throw new ServiceFailure("2161", e.getMessage());
813
		} catch (SAXException e) {
814
			throw new ServiceFailure("2161", e.getMessage());
815
		}
816
		
817
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
818
    }
819

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

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

    
924
    /**
925
     * Handle the getReplica action for the MN
926
     * @param id  the identifier for the object
927
     * @throws NotFound 
928
     * @throws ServiceFailure 
929
     * @throws NotImplemented 
930
     * @throws NotAuthorized 
931
     * @throws InvalidToken 
932
     * @throws InvalidRequest 
933
     */
934
    private void getReplica(String id) 
935
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
936
        ServiceFailure, NotFound {
937
        
938
        Identifier pid = new Identifier();
939
        pid.setValue(id);
940
        OutputStream out = null;
941
        InputStream dataBytes = null;
942
                
943
        try {
944
            // call the service
945
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
946

    
947
            response.setContentType("application/octet-stream");
948
            response.setStatus(200);
949
            out = response.getOutputStream();
950
            // write the object to the output stream
951
            IOUtils.copyLarge(dataBytes, out);
952
            
953
        } catch (IOException e) {
954
            String msg = "There was an error writing the output: " + e.getMessage();
955
            logMetacat.error(msg);
956
            throw new ServiceFailure("2181", msg);
957
        
958
        }
959

    
960
    }
961

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

    
999
        Identifier id = new Identifier();
1000
        id.setValue(pid);
1001

    
1002
        DescribeResponse dr = null;
1003
        try {
1004
        	dr = MNodeService.getInstance(request).describe(session, id);
1005
        } catch (BaseException e) {
1006
        	response.setStatus(e.getCode());
1007
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
1008
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
1009
            response.addHeader("DataONE-Exception-Description", e.getDescription());
1010
            response.addHeader("DataONE-Exception-PID", id.getValue());
1011
            return;
1012
		}
1013
        
1014
        response.setStatus(200);
1015
        
1016
        //response.addHeader("pid", pid);
1017
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
1018
        response.addHeader("Content-Length", dr.getContent_Length() + "");
1019
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
1020
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
1021
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
1022

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

    
1135
            out = response.getOutputStream();  
1136
            IOUtils.copyLarge(data, out);
1137
            
1138
        }
1139
        else
1140
        { //call listObjects with specified params
1141
            Date startTime = null;
1142
            Date endTime = null;
1143
            ObjectFormatIdentifier formatId = null;
1144
            Identifier identifier = null;
1145
            boolean replicaStatus = true;
1146
            int start = 0;
1147
            //TODO: make the max count into a const
1148
            int count = 1000;
1149
            Enumeration paramlist = request.getParameterNames();
1150
            while (paramlist.hasMoreElements()) 
1151
            { //parse the params and make the crud call
1152
                String name = (String) paramlist.nextElement();
1153
                String[] value = (String[])request.getParameterValues(name);
1154

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

    
1230
    /**
1231
     * Retrieve data package as Bagit zip
1232
     * @param pid
1233
     * @throws NotImplemented 
1234
     * @throws NotFound 
1235
     * @throws NotAuthorized 
1236
     * @throws ServiceFailure 
1237
     * @throws InvalidToken 
1238
     * @throws IOException 
1239
     * @throws InvalidRequest 
1240
     */
1241
    protected void getPackage(String format, String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1242

    
1243
        Identifier id = new Identifier();
1244
        id.setValue(pid);
1245
        ObjectFormatIdentifier formatId = null;
1246
        if (format != null) {
1247
        	formatId = new ObjectFormatIdentifier();
1248
        	formatId.setValue(format);
1249
        }
1250
		InputStream is = MNodeService.getInstance(request).getPackage(session, formatId , id);
1251
        
1252
        // use the provided filename
1253
        String filename = null;
1254
        if (is instanceof DeleteOnCloseFileInputStream) {
1255
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1256
        } else {
1257
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1258
        }
1259
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1260
        response.setContentType("application/zip");
1261
        response.setStatus(200);
1262
        OutputStream out = response.getOutputStream();
1263
        
1264
        // write it to the output stream
1265
        IOUtils.copyLarge(is, out);
1266
   }
1267
    
1268
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1269
			NotAuthorized, NotFound, NotImplemented, IOException,
1270
			JiBXException, InvalidRequest, IdentifierNotUnique,
1271
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1272

    
1273
		// publish the object
1274
		Identifier originalIdentifier = new Identifier();
1275
		originalIdentifier.setValue(pid);
1276
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1277

    
1278
		response.setStatus(200);
1279
		response.setContentType("text/xml");
1280
		OutputStream out = response.getOutputStream();
1281

    
1282
		// write new identifier to the output stream
1283
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1284
	}
1285
    
1286
    /**
1287
     * Retrieve System Metadata
1288
     * @param pid
1289
     * @throws InvalidToken
1290
     * @throws ServiceFailure
1291
     * @throws NotAuthorized
1292
     * @throws NotFound
1293
     * @throws InvalidRequest
1294
     * @throws NotImplemented
1295
     * @throws IOException
1296
     * @throws JiBXException
1297
     */
1298
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1299

    
1300
        Identifier id = new Identifier();
1301
        id.setValue(pid);
1302
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1303
        
1304
        response.setContentType("text/xml");
1305
        response.setStatus(200);
1306
        OutputStream out = response.getOutputStream();
1307
        
1308
        // Serialize and write it to the output stream
1309
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1310
   }
1311
    
1312
    
1313
    /**
1314
     * Inserts or updates the object
1315
     * 
1316
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1317
     *               is the existing pid.  If insert, the pid is the new one
1318
     * @throws InvalidRequest 
1319
     * @throws ServiceFailure 
1320
     * @throws JiBXException 
1321
     * @throws NotImplemented 
1322
     * @throws InvalidSystemMetadata 
1323
     * @throws InsufficientResources 
1324
     * @throws UnsupportedType 
1325
     * @throws IdentifierNotUnique 
1326
     * @throws NotAuthorized 
1327
     * @throws InvalidToken 
1328
     * @throws NotFound 
1329
     * @throws IOException 
1330
     * @throws IllegalAccessException 
1331
     * @throws InstantiationException 
1332
     */
1333
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1334
       
1335
    	// Read the incoming data from its Mime Multipart encoding
1336
    	Map<String, File> files = collectMultipartFiles();
1337
               
1338
    	Identifier pid = new Identifier();
1339
        if (trailingPid == null) {
1340
	        // get the pid string from the body and set the value
1341
	        String pidString = multipartparams.get("pid").get(0);
1342
	        if (pidString != null) {
1343
            pid.setValue(pidString);
1344
            
1345
          } else {
1346
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1347
              
1348
          }
1349
        } else {
1350
        	// use the pid included in the URL
1351
        	pid.setValue(trailingPid);
1352
        }
1353
        logMetacat.debug("putObject with pid " + pid.getValue());
1354
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1355

    
1356
        InputStream object = null;
1357
        InputStream sysmeta = null;
1358
        File smFile = files.get("sysmeta");
1359
        sysmeta = new FileInputStream(smFile);
1360
        File objFile = files.get("object");
1361
        object = new FileInputStream(objFile);
1362
        
1363
        // ensure we have the object bytes
1364
        if  ( objFile == null ) {
1365
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1366
            
1367
        }
1368
        
1369
        // ensure we have the system metadata
1370
        if  ( smFile == null ) {
1371
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1372
            
1373
        }
1374
        
1375
        response.setStatus(200);
1376
        response.setContentType("text/xml");
1377
        OutputStream out = response.getOutputStream();
1378
        
1379
        if (action.equals(FUNCTION_NAME_INSERT)) { 
1380
            // handle inserts
1381
            logMetacat.debug("Commence creation...");
1382
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1383

    
1384
            logMetacat.debug("creating object with pid " + pid.getValue());
1385
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1386
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1387
            
1388
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1389
        	// handle updates
1390
        	
1391
            // construct pids
1392
            Identifier newPid = null;
1393
            try {
1394
            	String newPidString = multipartparams.get("newPid").get(0);
1395
            	newPid = new Identifier();
1396
            	newPid.setValue(newPidString);
1397
            } catch (Exception e) {
1398
				logMetacat.error("Could not get newPid from request");
1399
			}
1400
            logMetacat.debug("Commence update...");
1401
            
1402
            // get the systemmetadata object
1403
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1404

    
1405
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1406
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1407
        } else {
1408
            throw new InvalidRequest("1000", "Operation must be create or update.");
1409
        }
1410
   
1411
    }
1412

    
1413
    /**
1414
     * Handle delete 
1415
     * @param pid ID of data object to be deleted
1416
     * @throws IOException
1417
     * @throws InvalidRequest 
1418
     * @throws NotImplemented 
1419
     * @throws NotFound 
1420
     * @throws NotAuthorized 
1421
     * @throws ServiceFailure 
1422
     * @throws InvalidToken 
1423
     * @throws JiBXException 
1424
     */
1425
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1426
    {
1427

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

    
1432
        Identifier id = new Identifier();
1433
        id.setValue(pid);
1434

    
1435
        logMetacat.debug("Calling delete");
1436
        MNodeService.getInstance(request).delete(session, id);
1437
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1438
        
1439
    }
1440
    
1441
    /**
1442
     * Archives the given pid
1443
     * @param pid
1444
     * @throws InvalidToken
1445
     * @throws ServiceFailure
1446
     * @throws NotAuthorized
1447
     * @throws NotFound
1448
     * @throws NotImplemented
1449
     * @throws IOException
1450
     * @throws JiBXException
1451
     */
1452
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1453

    
1454
        OutputStream out = response.getOutputStream();
1455
        response.setStatus(200);
1456
        response.setContentType("text/xml");
1457

    
1458
        Identifier id = new Identifier();
1459
        id.setValue(pid);
1460

    
1461
        logMetacat.debug("Calling archive");
1462
        MNodeService.getInstance(request).archive(session, id);
1463
        
1464
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1465
        
1466
    }
1467

    
1468
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1469
		
1470
		// Read the incoming data from its Mime Multipart encoding
1471
		logMetacat.debug("Disassembling MIME multipart form");
1472
		InputStream sf = null;
1473
	
1474
		// handle MMP inputs
1475
		File tmpDir = getTempDirectory();
1476
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1477
		MultipartRequestResolver mrr = 
1478
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), MAX_UPLOAD_SIZE, 0);
1479
		MultipartRequest mr = null;
1480
		try {
1481
			mr = mrr.resolveMultipart(request);
1482
		} catch (Exception e) {
1483
			throw new ServiceFailure("2161", 
1484
					"Could not resolve multipart: " + e.getMessage());
1485
		}
1486
		logMetacat.debug("resolved multipart request");
1487
		Map<String, File> files = mr.getMultipartFiles();
1488
		if (files == null || files.keySet() == null) {
1489
			throw new InvalidRequest("2163",
1490
					"must have multipart file with name 'message'");
1491
		}
1492
		logMetacat.debug("got multipart files");
1493
	
1494
		multipartparams = mr.getMultipartParameters();
1495
	
1496
		File sfFile = files.get("message");
1497
		if (sfFile == null) {
1498
			throw new InvalidRequest("2163",
1499
					"Missing the required file-part 'message' from the multipart request.");
1500
		}
1501
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1502
		sf = new FileInputStream(sfFile);
1503
	
1504
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1505
		return syncFailed;
1506
	}
1507
	
1508
	/**
1509
	 * Update the system metadata for a specified identifier
1510
	 * @throws ServiceFailure
1511
	 * @throws InvalidRequest
1512
	 * @throws InstantiationException
1513
	 * @throws IllegalAccessException
1514
	 * @throws IOException
1515
	 * @throws JiBXException
1516
	 * @throws NotImplemented
1517
	 * @throws NotAuthorized
1518
	 * @throws InvalidSystemMetadata
1519
	 * @throws InvalidToken
1520
	 */
1521
	protected void updateSystemMetadata() throws ServiceFailure, InvalidRequest, 
1522
	                        InstantiationException, IllegalAccessException, IOException, JiBXException, NotImplemented, 
1523
	                        NotAuthorized, InvalidSystemMetadata, InvalidToken {
1524
	    // Read the incoming data from its Mime Multipart encoding
1525
        Map<String, File> files = collectMultipartFiles();
1526
        
1527
        // get the encoded pid string from the body and make the object
1528
        String pidString = multipartparams.get("pid").get(0);
1529
        Identifier pid = new Identifier();
1530
        pid.setValue(pidString);
1531
        
1532
        logMetacat.debug("updateSystemMetadata: " + pid);
1533

    
1534
        // get the system metadata from the request
1535
        File smFile = files.get("sysmeta");
1536
        FileInputStream sysmeta = new FileInputStream(smFile);
1537
        SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1538

    
1539
        logMetacat.debug("updating system metadata with pid " + pid.getValue());
1540
        
1541
        MNodeService.getInstance(request).updateSystemMetadata(session, pid, systemMetadata);
1542
	}
1543

    
1544
}
(3-3/4)