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: 2011-11-14 16:00:19 -0800 (Mon, 14 Nov 2011) $'
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;
24

    
25
import java.io.ByteArrayInputStream;
26
import java.io.File;
27
import java.io.FileInputStream;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.text.DateFormat;
32
import java.text.ParseException;
33
import java.text.SimpleDateFormat;
34
import java.util.Date;
35
import java.util.Enumeration;
36
import java.util.Map;
37
import java.util.TimeZone;
38

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

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

    
83
import edu.ucsb.nceas.metacat.dataone.CNodeService;
84
import edu.ucsb.nceas.metacat.dataone.MNodeService;
85

    
86
/**
87
 * MN REST service implementation handler
88
 * 
89
 * ******************
90
 * MNCore -- DONE
91
 * 		ping() - GET /d1/mn/monitor/ping
92
 * 		log() - GET /d1/mn/log
93
 * 		**getObjectStatistics() - GET /d1/mn/monitor/object
94
 * 		getOperationsStatistics - GET /d1/mn/monitor/event
95
 * 		**getStatus - GET /d1/mn/monitor/status
96
 * 		getCapabilities() - GET /d1/mn/ and /d1/mn/node
97
 * 	
98
 * 	MNRead -- DONE
99
 * 		get() - GET /d1/mn/object/PID
100
 * 		getSystemMetadata() - GET /d1/mn/meta/PID
101
 * 		describe() - HEAD /d1/mn/object/PID
102
 * 		getChecksum() - GET /d1/mn/checksum/PID
103
 * 		listObjects() - GET /d1/mn/object
104
 * 		synchronizationFailed() - POST /d1/mn/error
105
 * 	
106
 * 	MNAuthorization -- DONE
107
 * 		isAuthorized() - GET /d1/mn/isAuthorized/PID
108
 * 		setAccessPolicy() - PUT /d1/mn/accessRules/PID
109
 * 		
110
 * 	MNStorage - DONE
111
 * 		create() - POST /d1/mn/object/PID
112
 * 		update() - PUT /d1/mn/object/PID
113
 * 		delete() - DELETE /d1/mn/object/PID
114
 *    systemMetadataChanged() - POST /dirtySystemMetadata/PID
115
 * 	
116
 * 	MNReplication
117
 * 		replicate() - POST /d1/mn/replicate
118
 *    getReplica() - GET /d1/mn/replica
119
 * 
120
 * ******************
121
 * @author leinfelder
122
 *
123
 */
124
public class MNResourceHandler extends D1ResourceHandler{
125

    
126
    // MN-specific API Resources
127
    protected static final String RESOURCE_MONITOR = "monitor";
128
    protected static final String RESOURCE_REPLICATE = "replicate";
129
    protected static final String RESOURCE_REPLICAS = "replica";
130
    protected static final String RESOURCE_NODE = "node";
131
    protected static final String RESOURCE_ERROR = "error";
132
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
133

    
134
    /**
135
     * Initializes new instance by setting servlet context,request and response
136
     * */
137
    public MNResourceHandler(ServletContext servletContext,
138
            HttpServletRequest request, HttpServletResponse response) {
139
    	super(servletContext, request, response);
140
        logMetacat = Logger.getLogger(MNResourceHandler.class);
141
    }
142

    
143
    /**
144
     * This function is called from REST API servlet and handles each request to the servlet 
145
     * 
146
     * @param httpVerb (GET, POST, PUT or DELETE)
147
     */
148
    @Override
149
    public void handle(byte httpVerb) {
150
    	// prepare the handler
151
    	super.handle(httpVerb);
152
    	
153
        try {
154
        	// get the resource
155
            String resource = request.getPathInfo();
156
            resource = resource.substring(resource.indexOf("/") + 1);
157
            
158
            // default to node info
159
            if (resource.equals("")) {
160
                resource = RESOURCE_NODE;
161
            }
162
            
163
            // get the rest of the path info
164
            String extra = null;
165
                        
166
            logMetacat.debug("handling verb " + httpVerb + " request with resource '" + resource + "'");
167
            logMetacat.debug("resource: '" + resource + "'");
168
            boolean status = false;
169
            
170
            if (resource != null) {
171

    
172
                if (resource.startsWith(RESOURCE_NODE)) {
173
                    // node response
174
                    node();
175
                    status = true;
176
                } else if (resource.startsWith(RESOURCE_ACCESS_RULES)) {
177
                    if (httpVerb == PUT) {
178
                    	// after the command
179
                        extra = parseTrailing(resource, RESOURCE_ACCESS_RULES);
180
	                	// set the access rules
181
	                    setAccess(extra);
182
	                    status = true;
183
	                    logMetacat.debug("done setting access");
184
                    }
185
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
186
                    if (httpVerb == GET) {
187
                    	// after the command
188
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
189
	                	// check the access rules
190
	                    isAuthorized(extra);
191
	                    status = true;
192
	                    logMetacat.debug("done getting access");
193
                    }
194
                } else if (resource.startsWith(RESOURCE_META)) {
195
                    logMetacat.debug("Using resource 'meta'");
196
                    // get
197
                    if (httpVerb == GET) {
198
                    	// after the command
199
                        extra = parseTrailing(resource, RESOURCE_META);
200
                        getSystemMetadataObject(extra);
201
                        status = true;
202
                    }
203
                    
204
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
205
                    logMetacat.debug("Using resource 'object'");
206
                    // after the command
207
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
208
                    logMetacat.debug("objectId: " + extra);
209
                    logMetacat.debug("verb:" + httpVerb);
210

    
211
                    if (httpVerb == GET) {
212
                        getObject(extra);
213
                        status = true;
214
                    } else if (httpVerb == POST) {
215
                        putObject(extra, FUNCTION_NAME_INSERT);
216
                        status = true;
217
                    } else if (httpVerb == PUT) {
218
                        putObject(extra, FUNCTION_NAME_UPDATE);
219
                        status = true;
220
                    } else if (httpVerb == DELETE) {
221
                        deleteObject(extra);
222
                        status = true;
223
                    } else if (httpVerb == HEAD) {
224
                        describeObject(extra);
225
                        status = true;
226
                    }
227
                  
228
                } else if (resource.startsWith(RESOURCE_LOG)) {
229
                    logMetacat.debug("Using resource 'log'");
230
                    // handle log events
231
                    if (httpVerb == GET) {
232
                        getLog();
233
                        status = true;
234
                    }
235
                } else if (resource.startsWith(RESOURCE_CHECKSUM)) {
236
                    logMetacat.debug("Using resource 'checksum'");
237
                    // handle checksum requests
238
                    if (httpVerb == GET) {
239
                    	// after the command
240
                        extra = parseTrailing(resource, RESOURCE_CHECKSUM);
241
                        checksum(extra);
242
                        status = true;
243
                    }
244
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
245
                    // there are various parts to monitoring
246
                    if (httpVerb == GET) {
247
                    	// after the command
248
                        extra = parseTrailing(resource, RESOURCE_MONITOR);
249
                    	// health monitoring calls
250
                        status = monitor(extra);
251
                    }
252
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
253
                	if (httpVerb == POST) {
254
	                    logMetacat.debug("processing replicate request");
255
	                    replicate();
256
	                    status = true;
257
                	}
258
                } else if (resource.startsWith(RESOURCE_ERROR)) {
259
	                // sync error
260
	                if (httpVerb == POST) {
261
	                    syncError();
262
	                    status = true;
263
	                }
264
                } else if (resource.startsWith(RESOURCE_META_CHANGED)) {
265
                    // system metadata changed
266
                    if (httpVerb == POST) {
267
                        systemMetadataChanged();
268
                        status = true;
269
                    }
270
                } else if (resource.startsWith(RESOURCE_REPLICAS)) {
271
                    // get replica
272
                    if (httpVerb == GET) {
273
                        extra = parseTrailing(resource, RESOURCE_REPLICAS);
274
                        getReplica(extra);
275
                        status = true;
276
                    }
277
                }
278
                
279
                if (!status) {
280
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
281
                }
282
            } else {
283
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
284
            }
285
        } catch (BaseException be) {
286
        	// report Exceptions as clearly as possible
287
        	OutputStream out = null;
288
			try {
289
				out = response.getOutputStream();
290
			} catch (IOException e) {
291
				logMetacat.error("Could not get output stream from response", e);
292
			}
293
            serializeException(be, out);
294
        } catch (Exception e) {
295
            // report Exceptions as clearly and generically as possible
296
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
297
        	OutputStream out = null;
298
			try {
299
				out = response.getOutputStream();
300
			} catch (IOException ioe) {
301
				logMetacat.error("Could not get output stream from response", ioe);
302
			}
303
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
304
            serializeException(se, out);
305
        }
306
    }
307
    
308

    
309
    /**
310
     * Handles notification of system metadata changes for the given identifier
311
     * 
312
     * @param id  the identifier for the object
313
     * @throws InvalidToken 
314
     * @throws InvalidRequest 
315
     * @throws NotAuthorized 
316
     * @throws ServiceFailure 
317
     * @throws NotImplemented 
318
     */
319
    private void systemMetadataChanged() 
320
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, 
321
        InvalidToken {
322

    
323
        long serialVersion = 0L;
324
        String serialVersionStr = null;
325
        Date dateSysMetaLastModified = null;
326
        String dateSysMetaLastModifiedStr = null;
327
        Identifier pid = null;
328
        
329
        // get the pid
330
        try {
331
        	String id = params.get("pid")[0];
332
        	pid = new Identifier();
333
            pid.setValue(id);            
334
        } catch (NullPointerException e) {
335
            String msg = "The 'pid' must be provided as a parameter and was not.";
336
            logMetacat.error(msg);
337
            throw new InvalidRequest("1334", msg);
338
        }      
339
        
340
        // get the serialVersion
341
        try {
342
            serialVersionStr = params.get("serialVersion")[0];
343
            serialVersion = new Long(serialVersionStr).longValue();
344
            
345
        } catch (NullPointerException e) {
346
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
347
            logMetacat.error(msg);
348
            throw new InvalidRequest("1334", msg);
349
            
350
        }       
351
        
352
        // get the dateSysMetaLastModified
353
        try {
354
            dateSysMetaLastModifiedStr = params.get("dateSysMetaLastModified")[0];
355
            dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
356
            
357
        } catch (NullPointerException e) {
358
            String msg = 
359
                "The 'dateSysMetaLastModified' must be provided as a " + 
360
                "parameter and was not, or was an invalid representation of the timestamp.";
361
            logMetacat.error(msg);
362
            throw new InvalidRequest("1334", msg);
363
            
364
        }       
365
        
366
        // call the service
367
        MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
368
        response.setStatus(200);
369
    }
370

    
371
    /**
372
     * Checks the access policy
373
     * @param id
374
     * @return
375
     * @throws ServiceFailure
376
     * @throws InvalidToken
377
     * @throws NotFound
378
     * @throws NotAuthorized
379
     * @throws NotImplemented
380
     * @throws InvalidRequest
381
     */
382
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
383
		Identifier pid = new Identifier();
384
		pid.setValue(id);
385
		Permission permission = null;
386
		try {
387
			String perm = params.get("action")[0];
388
			permission = Permission.convert(perm);
389
		} catch (Exception e) {
390
			logMetacat.warn("No permission specified");
391
		}
392
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
393
		response.setStatus(200);
394
		response.setContentType("text/xml");
395
		return result;
396
    }
397
    
398
    /**
399
     * Processes failed synchronization message
400
     * @throws NotImplemented
401
     * @throws ServiceFailure
402
     * @throws NotAuthorized
403
     * @throws InvalidRequest
404
     * @throws JiBXException
405
     * @throws IllegalAccessException 
406
     * @throws InstantiationException 
407
     * @throws IOException 
408
     */
409
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
410
    	SynchronizationFailed syncFailed = null;
411
		try {
412
			syncFailed = collectSynchronizationFailed();
413
		} catch (ParserConfigurationException e) {
414
			throw new ServiceFailure("2161", e.getMessage());
415
		} catch (SAXException e) {
416
			throw new ServiceFailure("2161", e.getMessage());
417
		}
418
		
419
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
420
    }
421
    
422

    
423
    /**
424
     * Handles the monitoring resources
425
     * @return
426
     * @throws NotFound
427
     * @throws ParseException
428
     * @throws NotImplemented
429
     * @throws ServiceFailure
430
     * @throws NotAuthorized
431
     * @throws InvalidRequest
432
     * @throws InsufficientResources
433
     * @throws UnsupportedType
434
     * @throws IOException
435
     * @throws JiBXException
436
     */
437
    private boolean monitor(String pathInfo) 
438
      throws NotFound, ParseException, NotImplemented, ServiceFailure, 
439
      NotAuthorized, InvalidRequest, InsufficientResources, UnsupportedType, 
440
      IOException, JiBXException {
441
    	logMetacat.debug("processing monitor request");
442
        
443
        logMetacat.debug("verb is GET");
444
        logMetacat.debug("pathInfo is " + pathInfo);
445
        
446
        if (pathInfo.toLowerCase().equals("ping")) {
447
            logMetacat.debug("processing ping request");
448
            boolean result = MNodeService.getInstance(request).ping();
449
            return result;
450
            
451
        } else if (pathInfo.toLowerCase().equals("status")) {
452
            logMetacat.debug("processing status request");
453
            // TODO: implement in MNCore
454
            //MNodeService.getInstance().getStatus();
455
            return false;
456
            
457
        } else if (pathInfo.toLowerCase().equals("object")) {
458
            logMetacat.debug("processing object request");
459
            Identifier pid = null;
460
            ObjectFormat format = null;
461
            if (params.containsKey("format")) {
462
                String f = params.get("format")[0];
463
                format = ObjectFormatCache.getInstance().getFormat(f);
464
            }
465
            if (params.containsKey("pid")) {
466
                String id = params.get("pid")[0];
467
                pid = new Identifier();
468
                pid.setValue(id);
469
            }
470
            
471
            // TODO: implement in MNCore
472
            //ObjectStatistics objectStats = MNodeService.getInstance().getObjectStatistics(format, pid);
473
            return false;
474
            
475
        } else if (pathInfo.toLowerCase().equals("event")) {
476
            logMetacat.debug("processing event request");
477
            ObjectFormatIdentifier fmtid = null;
478
            String fromDateStr = null;
479
            Date fromDate = null;
480
            String toDateStr = null;
481
            Date toDate = null;
482
            String requestor = null;
483
            Subject subject = null;
484
            String eventName = null;
485
            Event event = null;
486

    
487
            if (params.containsKey("formatId")) {
488
                String f = params.get("formatId")[0];
489
                fmtid = ObjectFormatCache.getInstance().getFormat(f).getFormatId();
490
            }
491
            
492
            if (params.containsKey("fromDate")) {
493
                fromDateStr = params.get("fromDate")[0];
494
                fromDate = getDateAsUTC(fromDateStr);
495
            }
496
            
497
            if (params.containsKey("toDate")) {
498
              toDateStr = params.get("toDate")[0];
499
              toDate = getDateAsUTC(toDateStr);
500
            }
501
            
502
            if (params.containsKey("requestor")) {
503
            	requestor = params.get("requestor")[0];
504
            	subject = new Subject();
505
            	subject.setValue(requestor);
506
            }
507
            
508
            if (params.containsKey("event")) {
509
            	eventName = params.get("event")[0];
510
                event = Event.convert(eventName);
511
            }
512
            
513
            MonitorList monitorList = MNodeService.getInstance(request).getOperationStatistics(session, fromDate, toDate, subject, event, fmtid);
514
            
515
            OutputStream out = response.getOutputStream();
516
            response.setStatus(200);
517
            response.setContentType("text/xml");
518
            
519
            TypeMarshaller.marshalTypeToOutputStream(monitorList, out);
520
            
521
            return true;
522
            
523
        }
524
        
525
        return false;
526
    }
527
    
528
    /*
529
     * Parse an ISO8601 date string, returning a Date in UTC time if the string
530
     * ends in 'Z'.
531
     * 
532
     * @param fromDateStr -  the date string to be parsed
533
     * @return date -  the date object represented by the string
534
     */
535
    private Date getDateAsUTC(String fromDateStr)
536
      throws ParseException {
537

    
538
    	Date date = null;
539
    	
540
    	try {
541
    		// try the expected date format
542
        // a date format for date string arguments
543
        DateFormat dateFormat = 
544
        	new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
545

    
546
	      date = dateFormat.parse(fromDateStr);
547
      
548
    	} catch (ParseException e) {
549
    		// try the date with the UTC indicator
550
        DateFormat utcDateFormat = 
551
        	new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
552
        utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
553
        date = utcDateFormat.parse(fromDateStr);
554
        
555
      }
556
    	
557
    	return date;
558
    }
559

    
560
		/**
561
     * Calculate the checksum 
562
     * @throws NotImplemented
563
     * @throws JiBXException
564
     * @throws IOException
565
     * @throws InvalidToken
566
     * @throws ServiceFailure
567
     * @throws NotAuthorized
568
     * @throws NotFound
569
     * @throws InvalidRequest
570
     */
571
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
572
    	String checksumAlgorithm = "MD5";
573
        
574
        Identifier pidid = new Identifier();
575
        pidid.setValue(pid);
576
        try {
577
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
578
        } catch(Exception e) {
579
            //do nothing.  default to MD5
580
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
581
        }
582
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
583
        
584
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
585
        logMetacat.debug("got checksum " + c.getValue());
586
        response.setStatus(200);
587
        logMetacat.debug("serializing response");
588
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
589
        logMetacat.debug("done serializing response.");
590
        
591
    }
592
    
593
	/**
594
     * handle the replicate action for MN
595
	 * @throws JiBXException 
596
	 * @throws FileUploadException 
597
	 * @throws IOException 
598
	 * @throws InvalidRequest 
599
	 * @throws ServiceFailure 
600
	 * @throws UnsupportedType 
601
	 * @throws InsufficientResources 
602
	 * @throws NotAuthorized 
603
	 * @throws NotImplemented 
604
	 * @throws IllegalAccessException 
605
	 * @throws InstantiationException 
606
     */
607
    private void replicate() throws ServiceFailure, InvalidRequest, IOException, FileUploadException, JiBXException, NotImplemented, NotAuthorized, InsufficientResources, UnsupportedType, InstantiationException, IllegalAccessException {
608

    
609
        logMetacat.debug("in POST replicate()");
610
        
611
        //parse the systemMetadata
612
        SystemMetadata sysmeta = collectSystemMetadata();
613
        
614
        String sn = multipartparams.get("sourceNode").get(0);
615
        logMetacat.debug("sourceNode: " + sn);
616
        NodeReference sourceNode = new NodeReference();
617
        sourceNode.setValue(sn);
618
        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
619

    
620
        response.setStatus(200);
621

    
622
    }
623

    
624
    /**
625
     * Handle the getReplica action for the MN
626
     * @param id  the identifier for the object
627
     * @throws NotFound 
628
     * @throws ServiceFailure 
629
     * @throws NotImplemented 
630
     * @throws NotAuthorized 
631
     * @throws InvalidToken 
632
     * @throws InvalidRequest 
633
     */
634
    private void getReplica(String id) 
635
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented, 
636
        ServiceFailure, NotFound {
637
        
638
        Identifier pid = new Identifier();
639
        pid.setValue(id);
640
        OutputStream out = null;
641
        InputStream dataBytes = null;
642
                
643
        try {
644
            // call the service
645
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
646

    
647
            response.setContentType("application/octet-stream");
648
            response.setStatus(200);
649
            out = response.getOutputStream();
650
            // write the object to the output stream
651
            IOUtils.copyLarge(dataBytes, out);
652
            
653
        } catch (IOException e) {
654
            String msg = "There was an error writing the output: " + e.getMessage();
655
            logMetacat.error(msg);
656
            throw new ServiceFailure("2181", msg);
657
        
658
        }
659

    
660
    }
661

    
662
    /**
663
     * Get the Node information
664
     * 
665
     * @throws JiBXException
666
     * @throws IOException
667
     * @throws InvalidRequest 
668
     * @throws ServiceFailure 
669
     * @throws NotAuthorized 
670
     * @throws NotImplemented 
671
     */
672
    private void node() 
673
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
674
        
675
        Node n = MNodeService.getInstance(request).getCapabilities();
676
        
677
        response.setContentType("text/xml");
678
        response.setStatus(200);
679
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
680
        
681
    }
682
    
683
    /**
684
     * MN_crud.describe()
685
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
686
     * @param pid
687
     * @throws InvalidRequest 
688
     * @throws NotImplemented 
689
     * @throws NotFound 
690
     * @throws NotAuthorized 
691
     * @throws ServiceFailure 
692
     * @throws InvalidToken 
693
     */
694
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
695
    {
696
        response.setStatus(200);
697
        response.setContentType("text/xml");
698
        
699
        Identifier id = new Identifier();
700
        id.setValue(pid);
701

    
702
        DescribeResponse dr = MNodeService.getInstance(request).describe(session, id);
703
        //response.addHeader("pid", pid);
704
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
705
        response.addHeader("Content-Length", dr.getContent_Length() + "");
706
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
707
        response.addHeader("DataONE-fmtid", dr.getDataONE_ObjectFormatIdentifier().getValue());
708
        
709
    }
710
    
711
    /**
712
     * get the logs based on passed params.  Available 
713
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
714
     * for more info
715
     * @throws NotImplemented 
716
     * @throws InvalidRequest 
717
     * @throws NotAuthorized 
718
     * @throws ServiceFailure 
719
     * @throws InvalidToken 
720
     * @throws IOException 
721
     * @throws JiBXException 
722
     */
723
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
724
    {
725
            
726
        Date fromDate = null;
727
        Date toDate = null;
728
        Event event = null;
729
        Integer start = null;
730
        Integer count = null;
731
        
732
        try {
733
        	String fromDateS = params.get("fromDate")[0];
734
            logMetacat.debug("param fromDateS: " + fromDateS);
735
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
736
        } catch (Exception e) {
737
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
738
        }
739
        try {
740
        	String toDateS = params.get("toDate")[0];
741
            logMetacat.debug("param toDateS: " + toDateS);
742
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
743
        } catch (Exception e) {
744
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
745
		}
746
        try {
747
        	String eventS = params.get("event")[0];
748
            event = Event.convert(eventS);
749
        } catch (Exception e) {
750
        	logMetacat.warn("Could not parse event: " + e.getMessage());
751
		}
752
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
753
        
754
        try {
755
        	start =  Integer.parseInt(params.get("start")[0]);
756
        } catch (Exception e) {
757
			logMetacat.warn("Could not parse start: " + e.getMessage());
758
		}
759
        try {
760
        	count =  Integer.parseInt(params.get("count")[0]);
761
        } catch (Exception e) {
762
			logMetacat.warn("Could not parse count: " + e.getMessage());
763
		}
764
        
765
        logMetacat.debug("calling getLogRecords");
766
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, start, count);
767
        
768
        OutputStream out = response.getOutputStream();
769
        response.setStatus(200);
770
        response.setContentType("text/xml");
771
        
772
        TypeMarshaller.marshalTypeToOutputStream(log, out);
773
        
774
    }
775
    
776
    /**
777
     * Implements REST version of DataONE CRUD API --> get
778
     * @param pid ID of data object to be read
779
     * @throws NotImplemented 
780
     * @throws InvalidRequest 
781
     * @throws NotFound 
782
     * @throws NotAuthorized 
783
     * @throws ServiceFailure 
784
     * @throws InvalidToken 
785
     * @throws IOException 
786
     * @throws JiBXException 
787
     */
788
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
789
        OutputStream out = null;
790
        
791
        if (pid != null) { //get a specific document                
792
            Identifier id = new Identifier();
793
            id.setValue(pid);
794
                
795
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
796
            
797
            //set the content type
798
            if (sm.getFormatId().getValue().trim().equals(
799
            		ObjectFormatCache.getInstance().getFormat("text/csv").getFormatId().getValue()))
800
            {
801
                response.setContentType("text/csv");
802
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".csv");
803
            }
804
            else if (sm.getFormatId().getValue().trim().equals(
805
            		ObjectFormatCache.getInstance().getFormat("text/plain").getFormatId().getValue()))
806
            {
807
                response.setContentType("text/plain");
808
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".txt");
809
            } 
810
            else if (sm.getFormatId().getValue().trim().equals(
811
            		ObjectFormatCache.getInstance().getFormat("application/octet-stream").getFormatId().getValue()))
812
            {
813
                response.setContentType("application/octet-stream");
814
            }
815
            else
816
            {
817
                response.setContentType("text/xml");
818
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".xml");
819
            }
820
            
821
            InputStream data = MNodeService.getInstance(request).get(session, id);
822

    
823
            out = response.getOutputStream();  
824
            IOUtils.copyLarge(data, out);
825
            
826
        }
827
        else
828
        { //call listObjects with specified params
829
            Date startTime = null;
830
            Date endTime = null;
831
            ObjectFormat objectFormat = null;
832
            boolean replicaStatus = false;
833
            int start = 0;
834
            //TODO: make the max count into a const
835
            int count = 1000;
836
            Enumeration paramlist = request.getParameterNames();
837
            while (paramlist.hasMoreElements()) 
838
            { //parse the params and make the crud call
839
                String name = (String) paramlist.nextElement();
840
                String[] value = (String[])request.getParameterValues(name);
841

    
842
                if (name.equals("startTime") && value != null)
843
                {
844
                    try
845
                    {
846
                      //startTime = dateFormat.parse(value[0]);
847
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
848
                        //startTime = parseDateAndConvertToGMT(value[0]);
849
                    }
850
                    catch(Exception e)
851
                    {  //if we can't parse it, just don't use the startTime param
852
                        logMetacat.warn("Could not parse startTime: " + value[0]);
853
                        startTime = null;
854
                    }
855
                }
856
                else if(name.equals("endTime") && value != null)
857
                {
858
                    try
859
                    {
860
                      //endTime = dateFormat.parse(value[0]);
861
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
862
                        //endTime = parseDateAndConvertToGMT(value[0]);
863
                    }
864
                    catch(Exception e)
865
                    {  //if we can't parse it, just don't use the endTime param
866
                        logMetacat.warn("Could not parse endTime: " + value[0]);
867
                        endTime = null;
868
                    }
869
                }
870
                else if(name.equals("objectFormat") && value != null) 
871
                {
872
                    objectFormat = ObjectFormatCache.getInstance().getFormat(value[0]);
873
                }
874
                else if(name.equals("replicaStatus") && value != null)
875
                {
876
                    if(value != null && 
877
                       value.length > 0 && 
878
                       (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
879
                    {
880
                        replicaStatus = true;
881
                    }
882
                }
883
                else if(name.equals("start") && value != null)
884
                {
885
                    start = new Integer(value[0]).intValue();
886
                }
887
                else if(name.equals("count") && value != null)
888
                {
889
                    count = new Integer(value[0]).intValue();
890
                }
891
            }
892
            //make the crud call
893
            logMetacat.debug("session: " + session + " startTime: " + startTime +
894
                    " endtime: " + endTime + " objectFormat: " + 
895
                    objectFormat + " replicaStatus: " + replicaStatus + 
896
                    " start: " + start + " count: " + count);
897
           
898
            ObjectFormatIdentifier fmtid = null;
899
           
900
            if ( objectFormat != null ) {
901
          	 fmtid = objectFormat.getFormatId();
902
          	 
903
            }
904
            ObjectList ol = 
905
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime, 
906
               fmtid, replicaStatus, start, count);
907
           
908
            out = response.getOutputStream();  
909
            response.setStatus(200);
910
            response.setContentType("text/xml");
911
            // Serialize and write it to the output stream
912
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
913
            
914
        }
915
        
916
    }
917
    
918

    
919
    /**
920
     * Retrieve System Metadata
921
     * @param pid
922
     * @throws InvalidToken
923
     * @throws ServiceFailure
924
     * @throws NotAuthorized
925
     * @throws NotFound
926
     * @throws InvalidRequest
927
     * @throws NotImplemented
928
     * @throws IOException
929
     * @throws JiBXException
930
     */
931
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
932

    
933
        Identifier id = new Identifier();
934
        id.setValue(pid);
935
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
936
        
937
        response.setContentType("text/xml");
938
        response.setStatus(200);
939
        OutputStream out = response.getOutputStream();
940
        
941
        // Serialize and write it to the output stream
942
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
943
   }
944
    
945
    
946
    /**
947
     * Inserts or updates the object
948
     * 
949
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
950
     *               is the existing pid.  If insert, the pid is the new one
951
     * @throws InvalidRequest 
952
     * @throws ServiceFailure 
953
     * @throws JiBXException 
954
     * @throws NotImplemented 
955
     * @throws InvalidSystemMetadata 
956
     * @throws InsufficientResources 
957
     * @throws UnsupportedType 
958
     * @throws IdentifierNotUnique 
959
     * @throws NotAuthorized 
960
     * @throws InvalidToken 
961
     * @throws NotFound 
962
     * @throws IOException 
963
     * @throws IllegalAccessException 
964
     * @throws InstantiationException 
965
     */
966
    protected void putObject(String pid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
967
        logMetacat.debug("putObject with pid " + pid);
968
        logMetacat.debug("Entering putObject: " + pid + "/" + action);
969
        
970
        response.setStatus(200);
971
        response.setContentType("text/xml");
972
        OutputStream out = response.getOutputStream();
973
        
974
        // Read the incoming data from its Mime Multipart encoding
975
    	Map<String, File> files = collectMultipartFiles();
976
        InputStream object = null;
977
        InputStream sysmeta = null;
978

    
979
        File smFile = files.get("sysmeta");
980
        sysmeta = new FileInputStream(smFile);
981
        File objFile = files.get("object");
982
        object = new FileInputStream(objFile);
983
        
984
        if (action.equals(FUNCTION_NAME_INSERT)) { 
985
            // handle inserts
986
            logMetacat.debug("Commence creation...");
987
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
988

    
989
            Identifier id = new Identifier();
990
            id.setValue(pid);
991
            logMetacat.debug("creating object with pid " + pid);
992
            Identifier rId = MNodeService.getInstance(request).create(session, id, object, smd);
993
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
994
            
995
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
996
        	// handle updates
997
        	
998
            // construct pids
999
            Identifier newPid = new Identifier();
1000
            try {
1001
            	String newPidString = multipartparams.get("newPid").get(0);
1002
                newPid.setValue(newPidString);
1003
            } catch (Exception e) {
1004
				logMetacat.warn("newPid not given");
1005
			}
1006
            
1007
            Identifier obsoletedPid = new Identifier();
1008
            obsoletedPid.setValue(pid);
1009
           
1010
            logMetacat.debug("Commence update...");
1011
            
1012
            // get the systemmetadata object
1013
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1014

    
1015
            Identifier rId = MNodeService.getInstance(request).update(session, obsoletedPid, object, newPid, smd);
1016
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1017
        } else {
1018
            throw new InvalidRequest("1000", "Operation must be create or update.");
1019
        }
1020
            
1021
            
1022
    }
1023

    
1024
    /**
1025
     * Handle delete 
1026
     * @param pid ID of data object to be deleted
1027
     * @throws IOException
1028
     * @throws InvalidRequest 
1029
     * @throws NotImplemented 
1030
     * @throws NotFound 
1031
     * @throws NotAuthorized 
1032
     * @throws ServiceFailure 
1033
     * @throws InvalidToken 
1034
     * @throws JiBXException 
1035
     */
1036
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
1037
    {
1038

    
1039
        OutputStream out = response.getOutputStream();
1040
        response.setStatus(200);
1041
        response.setContentType("text/xml");
1042

    
1043
        Identifier id = new Identifier();
1044
        id.setValue(pid);
1045

    
1046
        logMetacat.debug("Calling delete");
1047
        MNodeService.getInstance(request).delete(session, id);
1048
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1049
        
1050
    }    
1051
    
1052
    /**
1053
     * set the access perms on a document
1054
     * @throws JiBXException 
1055
     * @throws InvalidRequest 
1056
     * @throws NotImplemented 
1057
     * @throws NotAuthorized 
1058
     * @throws NotFound 
1059
     * @throws ServiceFailure 
1060
     * @throws InvalidToken 
1061
     * @throws IllegalAccessException 
1062
     * @throws InstantiationException 
1063
     * @throws IOException 
1064
     * @throws SAXException 
1065
     * @throws ParserConfigurationException 
1066
     */
1067
    protected void setAccess(String pid) throws JiBXException, InvalidToken, ServiceFailure, NotFound, NotAuthorized, NotImplemented, InvalidRequest, IOException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException
1068
    {
1069
    
1070
        //String pid = params.get("pid")[0];
1071
        Identifier id = new Identifier();
1072
        id.setValue(pid);
1073
        
1074
        AccessPolicy accessPolicy = collectAccessPolicy();
1075
        MNodeService.getInstance(request).setAccessPolicy(session, id, accessPolicy);
1076
        
1077
        
1078
    }
1079

    
1080
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1081
		
1082
		// Read the incoming data from its Mime Multipart encoding
1083
		logMetacat.debug("Disassembling MIME multipart form");
1084
		InputStream sf = null;
1085
	
1086
		// handle MMP inputs
1087
		File tmpDir = getTempDirectory();
1088
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1089
		MultipartRequestResolver mrr = 
1090
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
1091
		MultipartRequest mr = null;
1092
		try {
1093
			mr = mrr.resolveMultipart(request);
1094
		} catch (Exception e) {
1095
			throw new ServiceFailure("2161", 
1096
					"Could not resolve multipart: " + e.getMessage());
1097
		}
1098
		logMetacat.debug("resolved multipart request");
1099
		Map<String, File> files = mr.getMultipartFiles();
1100
		if (files == null || files.keySet() == null) {
1101
			throw new InvalidRequest("2163",
1102
					"must have multipart file with name 'message'");
1103
		}
1104
		logMetacat.debug("got multipart files");
1105
	
1106
		multipartparams = mr.getMultipartParameters();
1107
	
1108
		File sfFile = files.get("message");
1109
		if (sfFile == null) {
1110
			throw new InvalidRequest("2163",
1111
					"Missing the required file-part 'message' from the multipart request.");
1112
		}
1113
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1114
		sf = new FileInputStream(sfFile);
1115
	
1116
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1117
		return syncFailed;
1118
	}
1119

    
1120
}
(8-8/9)