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-09-22 13:40:17 -0700 (Thu, 22 Sep 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.MNodeService;
84

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

    
123
    // MN-specific API Resources
124
    protected static final String RESOURCE_MONITOR = "monitor";
125
    protected static final String RESOURCE_REPLICATE = "replicate";
126
    protected static final String RESOURCE_NODE = "node";
127
    protected static final String RESOURCE_ERROR = "error";
128

    
129
    /**
130
     * Initializes new instance by setting servlet context,request and response
131
     * */
132
    public MNResourceHandler(ServletContext servletContext,
133
            HttpServletRequest request, HttpServletResponse response) {
134
    	super(servletContext, request, response);
135
        logMetacat = Logger.getLogger(MNResourceHandler.class);
136
    }
137

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

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

    
202
                    if (httpVerb == GET) {
203
                        getObject(extra);
204
                        status = true;
205
                    } else if (httpVerb == POST) {
206
                        putObject(extra, FUNCTION_NAME_INSERT);
207
                        status = true;
208
                    } else if (httpVerb == PUT) {
209
                        putObject(extra, FUNCTION_NAME_UPDATE);
210
                        status = true;
211
                    } else if (httpVerb == DELETE) {
212
                        deleteObject(extra);
213
                        status = true;
214
                    } else if (httpVerb == HEAD) {
215
                        describeObject(extra);
216
                        status = true;
217
                    }
218
                  
219
                } else if (resource.startsWith(RESOURCE_LOG)) {
220
                    logMetacat.debug("Using resource 'log'");
221
                    // handle log events
222
                    if (httpVerb == GET) {
223
                        getLog();
224
                        status = true;
225
                    }
226
                } else if (resource.startsWith(RESOURCE_CHECKSUM)) {
227
                    logMetacat.debug("Using resource 'checksum'");
228
                    // handle checksum requests
229
                    if (httpVerb == GET) {
230
                        checksum(extra);
231
                        status = true;
232
                    }
233
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
234
                    // there are various parts to monitoring
235
                    if (httpVerb == GET) {
236
                    	// health monitoring calls
237
                        status = monitor(extra);
238
                    }
239
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
240
                	if (httpVerb == POST) {
241
	                    logMetacat.debug("processing replicate request");
242
	                    replicate();
243
	                    status = true;
244
                	}
245
                } else if (resource.startsWith(RESOURCE_ERROR)) {
246
	                // sync error
247
	                if (httpVerb == POST) {
248
	                    syncError();
249
	                    status = true;
250
	                }
251
                }
252
                
253
                if (!status) {
254
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
255
                }
256
            } else {
257
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
258
            }
259
        } catch (BaseException be) {
260
        	// report Exceptions as clearly as possible
261
        	OutputStream out = null;
262
			try {
263
				out = response.getOutputStream();
264
			} catch (IOException e) {
265
				logMetacat.error("Could not get output stream from response", e);
266
			}
267
            serializeException(be, out);
268
        } catch (Exception e) {
269
            // report Exceptions as clearly and generically as possible
270
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
271
        	OutputStream out = null;
272
			try {
273
				out = response.getOutputStream();
274
			} catch (IOException ioe) {
275
				logMetacat.error("Could not get output stream from response", ioe);
276
			}
277
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
278
            serializeException(se, out);
279
        }
280
    }
281
    
282
    /**
283
     * Checks the access policy
284
     * @param id
285
     * @return
286
     * @throws ServiceFailure
287
     * @throws InvalidToken
288
     * @throws NotFound
289
     * @throws NotAuthorized
290
     * @throws NotImplemented
291
     * @throws InvalidRequest
292
     */
293
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
294
		Identifier pid = new Identifier();
295
		pid.setValue(id);
296
		Permission permission = null;
297
		try {
298
			String perm = params.get("permission")[0];
299
			permission = Permission.convert(perm);
300
		} catch (Exception e) {
301
			logMetacat.warn("No permission specified");
302
		}
303
		boolean result = MNodeService.getInstance().isAuthorized(session, pid, permission);
304
		response.setStatus(200);
305
		response.setContentType("text/xml");
306
		return result;
307
    }
308
    
309
    /**
310
     * Processes failed synchronization message
311
     * @throws NotImplemented
312
     * @throws ServiceFailure
313
     * @throws NotAuthorized
314
     * @throws InvalidRequest
315
     * @throws JiBXException
316
     * @throws IllegalAccessException 
317
     * @throws InstantiationException 
318
     * @throws IOException 
319
     */
320
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
321
    	SynchronizationFailed syncFailed = null;
322
		try {
323
			syncFailed = collectSynchronizationFailed();
324
		} catch (ParserConfigurationException e) {
325
			throw new ServiceFailure("2161", e.getMessage());
326
		} catch (SAXException e) {
327
			throw new ServiceFailure("2161", e.getMessage());
328
		}
329
		
330
		MNodeService.getInstance().synchronizationFailed(session, syncFailed);
331
    }
332
    
333
    protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
334
		
335
		// Read the incoming data from its Mime Multipart encoding
336
		logMetacat.debug("Disassembling MIME multipart form");
337
		InputStream sf = null;
338

    
339
		// handle MMP inputs
340
		File tmpDir = getTempDirectory();
341
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
342
		MultipartRequestResolver mrr = 
343
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
344
		MultipartRequest mr = null;
345
		try {
346
			mr = mrr.resolveMultipart(request);
347
		} catch (Exception e) {
348
			throw new ServiceFailure("2161", 
349
					"Could not resolve multipart: " + e.getMessage());
350
		}
351
		logMetacat.debug("resolved multipart request");
352
		Map<String, File> files = mr.getMultipartFiles();
353
		if (files == null || files.keySet() == null) {
354
			throw new InvalidRequest("2163",
355
					"must have multipart file with name 'message'");
356
		}
357
		logMetacat.debug("got multipart files");
358

    
359
		multipartparams = mr.getMultipartParameters();
360

    
361
		File sfFile = files.get("message");
362
		if (sfFile == null) {
363
			throw new InvalidRequest("2163",
364
					"Missing the required file-part 'message' from the multipart request.");
365
		}
366
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
367
		sf = new FileInputStream(sfFile);
368
	
369
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
370
		return syncFailed;
371
	}
372

    
373
    /**
374
     * Handles the monitoring resources
375
     * @return
376
     * @throws NotFound
377
     * @throws ParseException
378
     * @throws NotImplemented
379
     * @throws ServiceFailure
380
     * @throws NotAuthorized
381
     * @throws InvalidRequest
382
     * @throws InsufficientResources
383
     * @throws UnsupportedType
384
     * @throws IOException
385
     * @throws JiBXException
386
     */
387
    private boolean monitor(String pathInfo) 
388
      throws NotFound, ParseException, NotImplemented, ServiceFailure, 
389
      NotAuthorized, InvalidRequest, InsufficientResources, UnsupportedType, 
390
      IOException, JiBXException {
391
    	logMetacat.debug("processing monitor request");
392
        
393
        logMetacat.debug("verb is GET");
394
        logMetacat.debug("pathInfo is " + pathInfo);
395
        
396
        if (pathInfo.toLowerCase().equals("ping")) {
397
            logMetacat.debug("processing ping request");
398
            boolean result = MNodeService.getInstance().ping();
399
            return result;
400
            
401
        } else if (pathInfo.toLowerCase().equals("status")) {
402
            logMetacat.debug("processing status request");
403
            // TODO: implement in MNCore
404
            //MNodeService.getInstance().getStatus();
405
            return false;
406
            
407
        } else if (pathInfo.toLowerCase().equals("object")) {
408
            logMetacat.debug("processing object request");
409
            Identifier pid = null;
410
            ObjectFormat format = null;
411
            if (params.containsKey("format")) {
412
                String f = params.get("format")[0];
413
                format = ObjectFormatCache.getInstance().getFormat(f);
414
            }
415
            if (params.containsKey("pid")) {
416
                String id = params.get("pid")[0];
417
                pid = new Identifier();
418
                pid.setValue(id);
419
            }
420
            
421
            // TODO: implement in MNCore
422
            //ObjectStatistics objectStats = MNodeService.getInstance().getObjectStatistics(format, pid);
423
            return false;
424
            
425
        } else if (pathInfo.toLowerCase().equals("event")) {
426
            logMetacat.debug("processing event request");
427
            ObjectFormatIdentifier fmtid = null;
428
            String fromDateStr = null;
429
            Date fromDate = null;
430
            String toDateStr = null;
431
            Date toDate = null;
432
            String requestor = null;
433
            Subject subject = null;
434
            String eventName = null;
435
            Event event = null;
436

    
437
            if (params.containsKey("formatId")) {
438
                String f = params.get("formatId")[0];
439
                fmtid = ObjectFormatCache.getInstance().getFormat(f).getFmtid();
440
            }
441
            
442
            if (params.containsKey("fromDate")) {
443
                fromDateStr = params.get("fromDate")[0];
444
                fromDate = getDateAsUTC(fromDateStr);
445
            }
446
            
447
            if (params.containsKey("toDate")) {
448
              toDateStr = params.get("toDate")[0];
449
              toDate = getDateAsUTC(toDateStr);
450
            }
451
            
452
            if (params.containsKey("requestor")) {
453
            	requestor = params.get("requestor")[0];
454
            	subject = new Subject();
455
            	subject.setValue(requestor);
456
            }
457
            
458
            if (params.containsKey("event")) {
459
            	eventName = params.get("event")[0];
460
                event = Event.convert(eventName);
461
            }
462
            
463
            MonitorList monitorList = MNodeService.getInstance().getOperationStatistics(session, fromDate, toDate, subject, event, fmtid);
464
            
465
            OutputStream out = response.getOutputStream();
466
            response.setStatus(200);
467
            response.setContentType("text/xml");
468
            
469
            TypeMarshaller.marshalTypeToOutputStream(monitorList, out);
470
            
471
            return true;
472
            
473
        }
474
        
475
        return false;
476
    }
477
    
478
    /*
479
     * Parse an ISO8601 date string, returning a Date in UTC time if the string
480
     * ends in 'Z'.
481
     * 
482
     * @param fromDateStr -  the date string to be parsed
483
     * @return date -  the date object represented by the string
484
     */
485
    private Date getDateAsUTC(String fromDateStr)
486
      throws ParseException {
487

    
488
    	Date date = null;
489
    	
490
    	try {
491
    		// try the expected date format
492
        // a date format for date string arguments
493
        DateFormat dateFormat = 
494
        	new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
495

    
496
	      date = dateFormat.parse(fromDateStr);
497
      
498
    	} catch (ParseException e) {
499
    		// try the date with the UTC indicator
500
        DateFormat utcDateFormat = 
501
        	new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
502
        utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
503
        date = utcDateFormat.parse(fromDateStr);
504
        
505
      }
506
    	
507
    	return date;
508
    }
509

    
510
		/**
511
     * Calculate the checksum 
512
     * @throws NotImplemented
513
     * @throws JiBXException
514
     * @throws IOException
515
     * @throws InvalidToken
516
     * @throws ServiceFailure
517
     * @throws NotAuthorized
518
     * @throws NotFound
519
     * @throws InvalidRequest
520
     */
521
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
522
    	String checksumAlgorithm = "MD5";
523
        
524
        Identifier pidid = new Identifier();
525
        pidid.setValue(pid);
526
        try {
527
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
528
        } catch(Exception e) {
529
            //do nothing.  default to MD5
530
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
531
        }
532
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
533
        
534
        Checksum c = MNodeService.getInstance().getChecksum(session, pidid, checksumAlgorithm);
535
        logMetacat.debug("got checksum " + c.getValue());
536
        response.setStatus(200);
537
        logMetacat.debug("serializing response");
538
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
539
        logMetacat.debug("done serializing response.");
540
        
541
    }
542
    
543
	/**
544
     * handle the replicate action for MN
545
	 * @throws JiBXException 
546
	 * @throws FileUploadException 
547
	 * @throws IOException 
548
	 * @throws InvalidRequest 
549
	 * @throws ServiceFailure 
550
	 * @throws UnsupportedType 
551
	 * @throws InsufficientResources 
552
	 * @throws NotAuthorized 
553
	 * @throws NotImplemented 
554
	 * @throws IllegalAccessException 
555
	 * @throws InstantiationException 
556
     */
557
    private void replicate() throws ServiceFailure, InvalidRequest, IOException, FileUploadException, JiBXException, NotImplemented, NotAuthorized, InsufficientResources, UnsupportedType, InstantiationException, IllegalAccessException {
558

    
559
        logMetacat.debug("in POST replicate()");
560
        
561
        //parse the systemMetadata
562
        SystemMetadata sysmeta = collectSystemMetadata();
563
        
564
        String sn = multipartparams.get("sourceNode").get(0);
565
        logMetacat.debug("sourceNode: " + sn);
566
        NodeReference sourceNode = TypeMarshaller.unmarshalTypeFromStream(NodeReference.class, new ByteArrayInputStream(sn.getBytes("UTF-8")));
567
        
568
        MNodeService.getInstance().replicate(session, sysmeta, sourceNode);
569

    
570
        response.setStatus(200);
571

    
572
    }
573
    
574
    /**
575
     * Get the Node information
576
     * 
577
     * @throws JiBXException
578
     * @throws IOException
579
     * @throws InvalidRequest 
580
     * @throws ServiceFailure 
581
     * @throws NotAuthorized 
582
     * @throws NotImplemented 
583
     */
584
    private void node() 
585
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
586
        
587
        Node n = MNodeService.getInstance().getCapabilities();
588
        
589
        response.setContentType("text/xml");
590
        response.setStatus(200);
591
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
592
        
593
    }
594
    
595
    /**
596
     * MN_crud.describe()
597
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
598
     * @param pid
599
     * @throws InvalidRequest 
600
     * @throws NotImplemented 
601
     * @throws NotFound 
602
     * @throws NotAuthorized 
603
     * @throws ServiceFailure 
604
     * @throws InvalidToken 
605
     */
606
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
607
    {
608
        response.setStatus(200);
609
        response.setContentType("text/xml");
610
        
611
        Identifier id = new Identifier();
612
        id.setValue(pid);
613

    
614
        DescribeResponse dr = MNodeService.getInstance().describe(session, id);
615
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SZ");
616
        response.addHeader("pid", pid);
617
        response.addHeader("checksum", dr.getDataONE_Checksum().getValue());
618
        response.addHeader("checksum_algorithm", dr.getDataONE_Checksum().getAlgorithm());
619
        response.addHeader("content_length", dr.getContent_Length() + "");
620
        response.addHeader("last_modified", dateFormat.format(dr.getLast_Modified()));
621
        response.addHeader("format", dr.getDataONE_ObjectFormatIdentifier().getValue());
622
       
623
    }
624
    
625
    /**
626
     * get the logs based on passed params.  Available 
627
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
628
     * for more info
629
     * @throws NotImplemented 
630
     * @throws InvalidRequest 
631
     * @throws NotAuthorized 
632
     * @throws ServiceFailure 
633
     * @throws InvalidToken 
634
     * @throws IOException 
635
     * @throws JiBXException 
636
     */
637
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
638
    {
639
            
640
        Date fromDate = null;
641
        Date toDate = null;
642
        Event event = null;
643
        Integer start = null;
644
        Integer count = null;
645
        
646
        try {
647
        	String fromDateS = params.get("fromDate")[0];
648
            logMetacat.debug("param fromDateS: " + fromDateS);
649
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
650
        } catch (Exception e) {
651
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
652
        }
653
        try {
654
        	String toDateS = params.get("toDate")[0];
655
            logMetacat.debug("param toDateS: " + toDateS);
656
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
657
        } catch (Exception e) {
658
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
659
		}
660
        try {
661
        	String eventS = params.get("event")[0];
662
            event = Event.convert(eventS);
663
        } catch (Exception e) {
664
        	logMetacat.warn("Could not parse event: " + e.getMessage());
665
		}
666
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
667
        
668
        try {
669
        	start =  Integer.parseInt(params.get("start")[0]);
670
        } catch (Exception e) {
671
			logMetacat.warn("Could not parse start: " + e.getMessage());
672
		}
673
        try {
674
        	count =  Integer.parseInt(params.get("count")[0]);
675
        } catch (Exception e) {
676
			logMetacat.warn("Could not parse count: " + e.getMessage());
677
		}
678
        
679
        logMetacat.debug("calling getLogRecords");
680
        Log log = MNodeService.getInstance().getLogRecords(session, fromDate, toDate, event, start, count);
681
        
682
        OutputStream out = response.getOutputStream();
683
        response.setStatus(200);
684
        response.setContentType("text/xml");
685
        
686
        TypeMarshaller.marshalTypeToOutputStream(log, out);
687
        
688
    }
689
    
690
    /**
691
     * Implements REST version of DataONE CRUD API --> get
692
     * @param pid ID of data object to be read
693
     * @throws NotImplemented 
694
     * @throws InvalidRequest 
695
     * @throws NotFound 
696
     * @throws NotAuthorized 
697
     * @throws ServiceFailure 
698
     * @throws InvalidToken 
699
     * @throws IOException 
700
     * @throws JiBXException 
701
     */
702
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
703
        OutputStream out = null;
704
        
705
        if (pid != null) { //get a specific document                
706
            Identifier id = new Identifier();
707
            id.setValue(pid);
708
                
709
            SystemMetadata sm = MNodeService.getInstance().getSystemMetadata(session, id);
710
            
711
            //set the content type
712
            if (sm.getFmtid().getValue().trim().equals(
713
            		ObjectFormatCache.getInstance().getFormat("text/csv").getFmtid().getValue()))
714
            {
715
                response.setContentType("text/csv");
716
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".csv");
717
            }
718
            else if (sm.getFmtid().getValue().trim().equals(
719
            		ObjectFormatCache.getInstance().getFormat("text/plain").getFmtid().getValue()))
720
            {
721
                response.setContentType("text/plain");
722
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".txt");
723
            } 
724
            else if (sm.getFmtid().getValue().trim().equals(
725
            		ObjectFormatCache.getInstance().getFormat("application/octet-stream").getFmtid().getValue()))
726
            {
727
                response.setContentType("application/octet-stream");
728
            }
729
            else
730
            {
731
                response.setContentType("text/xml");
732
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".xml");
733
            }
734
            
735
            InputStream data = MNodeService.getInstance().get(session, id);
736

    
737
            out = response.getOutputStream();  
738
            IOUtils.copyLarge(data, out);
739
            
740
        }
741
        else
742
        { //call listObjects with specified params
743
            Date startTime = null;
744
            Date endTime = null;
745
            ObjectFormat objectFormat = null;
746
            boolean replicaStatus = false;
747
            int start = 0;
748
            //TODO: make the max count into a const
749
            int count = 1000;
750
            Enumeration paramlist = request.getParameterNames();
751
            while (paramlist.hasMoreElements()) 
752
            { //parse the params and make the crud call
753
                String name = (String) paramlist.nextElement();
754
                String[] value = (String[])request.getParameterValues(name);
755

    
756
                if (name.equals("startTime") && value != null)
757
                {
758
                    try
759
                    {
760
                      //startTime = dateFormat.parse(value[0]);
761
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
762
                        //startTime = parseDateAndConvertToGMT(value[0]);
763
                    }
764
                    catch(Exception e)
765
                    {  //if we can't parse it, just don't use the startTime param
766
                        logMetacat.warn("Could not parse startTime: " + value[0]);
767
                        startTime = null;
768
                    }
769
                }
770
                else if(name.equals("endTime") && value != null)
771
                {
772
                    try
773
                    {
774
                      //endTime = dateFormat.parse(value[0]);
775
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
776
                        //endTime = parseDateAndConvertToGMT(value[0]);
777
                    }
778
                    catch(Exception e)
779
                    {  //if we can't parse it, just don't use the endTime param
780
                        logMetacat.warn("Could not parse endTime: " + value[0]);
781
                        endTime = null;
782
                    }
783
                }
784
                else if(name.equals("objectFormat") && value != null) 
785
                {
786
                    objectFormat = ObjectFormatCache.getInstance().getFormat(value[0]);
787
                }
788
                else if(name.equals("replicaStatus") && value != null)
789
                {
790
                    if(value != null && 
791
                       value.length > 0 && 
792
                       (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
793
                    {
794
                        replicaStatus = true;
795
                    }
796
                }
797
                else if(name.equals("start") && value != null)
798
                {
799
                    start = new Integer(value[0]).intValue();
800
                }
801
                else if(name.equals("count") && value != null)
802
                {
803
                    count = new Integer(value[0]).intValue();
804
                }
805
            }
806
            //make the crud call
807
            logMetacat.debug("session: " + session + " startTime: " + startTime +
808
                    " endtime: " + endTime + " objectFormat: " + 
809
                    objectFormat + " replicaStatus: " + replicaStatus + 
810
                    " start: " + start + " count: " + count);
811
           
812
            ObjectFormatIdentifier fmtid = null;
813
           
814
            if ( objectFormat != null ) {
815
          	 fmtid = objectFormat.getFmtid();
816
          	 
817
            }
818
            ObjectList ol = 
819
           	 MNodeService.getInstance().listObjects(session, startTime, endTime, 
820
               fmtid, replicaStatus, start, count);
821
           
822
            out = response.getOutputStream();  
823
            response.setStatus(200);
824
            response.setContentType("text/xml");
825
            // Serialize and write it to the output stream
826
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
827
            
828
        }
829
        
830
    }
831
    
832

    
833
    /**
834
     * Retrieve System Metadata
835
     * @param pid
836
     * @throws InvalidToken
837
     * @throws ServiceFailure
838
     * @throws NotAuthorized
839
     * @throws NotFound
840
     * @throws InvalidRequest
841
     * @throws NotImplemented
842
     * @throws IOException
843
     * @throws JiBXException
844
     */
845
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
846

    
847
        Identifier id = new Identifier();
848
        id.setValue(pid);
849
        SystemMetadata sysmeta = MNodeService.getInstance().getSystemMetadata(session, id);
850
        
851
        response.setContentType("text/xml");
852
        response.setStatus(200);
853
        OutputStream out = response.getOutputStream();
854
        
855
        // Serialize and write it to the output stream
856
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
857
   }
858
    
859
    
860
    /**
861
     * Inserts or updates the object
862
     * 
863
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
864
     *               is the existing pid.  If insert, the pid is the new one
865
     * @throws InvalidRequest 
866
     * @throws ServiceFailure 
867
     * @throws JiBXException 
868
     * @throws NotImplemented 
869
     * @throws InvalidSystemMetadata 
870
     * @throws InsufficientResources 
871
     * @throws UnsupportedType 
872
     * @throws IdentifierNotUnique 
873
     * @throws NotAuthorized 
874
     * @throws InvalidToken 
875
     * @throws NotFound 
876
     * @throws IOException 
877
     * @throws IllegalAccessException 
878
     * @throws InstantiationException 
879
     */
880
    protected void putObject(String pid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
881
        logMetacat.debug("putObject with pid " + pid);
882
        logMetacat.debug("Entering putObject: " + pid + "/" + action);
883
        
884
        response.setStatus(200);
885
        response.setContentType("text/xml");
886
        OutputStream out = response.getOutputStream();
887
        
888
        // Read the incoming data from its Mime Multipart encoding
889
    	Map<String, File> files = collectMultipartFiles();
890
        InputStream object = null;
891
        InputStream sysmeta = null;
892

    
893
        File smFile = files.get("sysmeta");
894
        sysmeta = new FileInputStream(smFile);
895
        File objFile = files.get("object");
896
        object = new FileInputStream(objFile);
897
        
898
        if (action.equals(FUNCTION_NAME_INSERT)) { 
899
            // handle inserts
900
            logMetacat.debug("Commence creation...");
901
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
902

    
903
            Identifier id = new Identifier();
904
            id.setValue(pid);
905
            logMetacat.debug("creating object with pid " + pid);
906
            Identifier rId = MNodeService.getInstance().create(session, id, object, smd);
907
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
908
            
909
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
910
        	// handle updates
911
        	
912
            // construct pids
913
            Identifier newPid = new Identifier();
914
            try {
915
            	String newPidString = multipartparams.get("newPid").get(0);
916
                newPid.setValue(newPidString);
917
            } catch (Exception e) {
918
				logMetacat.warn("newPid not given");
919
			}
920
            
921
            Identifier obsoletedPid = new Identifier();
922
            obsoletedPid.setValue(pid);
923
           
924
            logMetacat.debug("Commence update...");
925
            
926
            // get the systemmetadata object
927
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
928

    
929
            Identifier rId = MNodeService.getInstance().update(session, newPid, object, obsoletedPid, smd);
930
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
931
        } else {
932
            throw new InvalidRequest("1000", "Operation must be create or update.");
933
        }
934
            
935
            
936
    }
937

    
938
    /**
939
     * Handle delete 
940
     * @param pid ID of data object to be deleted
941
     * @throws IOException
942
     * @throws InvalidRequest 
943
     * @throws NotImplemented 
944
     * @throws NotFound 
945
     * @throws NotAuthorized 
946
     * @throws ServiceFailure 
947
     * @throws InvalidToken 
948
     * @throws JiBXException 
949
     */
950
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
951
    {
952

    
953
        OutputStream out = response.getOutputStream();
954
        response.setStatus(200);
955
        response.setContentType("text/xml");
956

    
957
        Identifier id = new Identifier();
958
        id.setValue(pid);
959

    
960
        logMetacat.debug("Calling delete");
961
        MNodeService.getInstance().delete(session, id);
962
        TypeMarshaller.marshalTypeToOutputStream(id, out);
963
        
964
    }    
965
    
966
    /**
967
     * set the access perms on a document
968
     * @throws JiBXException 
969
     * @throws InvalidRequest 
970
     * @throws NotImplemented 
971
     * @throws NotAuthorized 
972
     * @throws NotFound 
973
     * @throws ServiceFailure 
974
     * @throws InvalidToken 
975
     * @throws IllegalAccessException 
976
     * @throws InstantiationException 
977
     * @throws IOException 
978
     */
979
    protected void setAccess() throws JiBXException, InvalidToken, ServiceFailure, NotFound, NotAuthorized, NotImplemented, InvalidRequest, IOException, InstantiationException, IllegalAccessException
980
    {
981
    
982
        String pid = params.get("pid")[0];
983
        Identifier id = new Identifier();
984
        id.setValue(pid);
985
        String accesspolicy = params.get("accesspolicy")[0];
986
        AccessPolicy accessPolicy = TypeMarshaller.unmarshalTypeFromStream(AccessPolicy.class, new ByteArrayInputStream(accesspolicy.getBytes("UTF-8")));
987
        MNodeService.getInstance().setAccessPolicy(session, id, accessPolicy);
988
        
989
        
990
    }
991

    
992
}
(8-8/11)