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-20 14:11:47 -0700 (Tue, 20 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.NodeList;
71
import org.dataone.service.types.v1.NodeReference;
72
import org.dataone.service.types.v1.ObjectFormat;
73
import org.dataone.service.types.v1.ObjectFormatIdentifier;
74
import org.dataone.service.types.v1.ObjectList;
75
import org.dataone.service.types.v1.Permission;
76
import org.dataone.service.types.v1.Subject;
77
import org.dataone.service.types.v1.SystemMetadata;
78
import org.dataone.service.util.DateTimeMarshaller;
79
import org.dataone.service.util.ExceptionHandler;
80
import org.dataone.service.util.TypeMarshaller;
81
import org.jibx.runtime.JiBXException;
82
import org.xml.sax.SAXException;
83

    
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
 * 	
115
 * 	MNReplication
116
 * 		replicate() - POST /d1/mn/replicate
117
 * 
118
 * ******************
119
 * @author leinfelder
120
 *
121
 */
122
public class MNResourceHandler extends D1ResourceHandler{
123

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

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

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

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

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

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

    
360
		multipartparams = mr.getMultipartParameters();
361

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

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

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

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

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

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

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

    
571
        response.setStatus(200);
572

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

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

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

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

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

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

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

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

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

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

    
957
        OutputStream out = response.getOutputStream();
958
        response.setStatus(200);
959
        response.setContentType("text/xml");
960

    
961
        Identifier id = new Identifier();
962
        id.setValue(pid);
963

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

    
996
}
(8-8/11)