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

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

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

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

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

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

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

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

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

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

    
955
        Identifier id = new Identifier();
956
        id.setValue(pid);
957

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

    
990
}
(8-8/11)