Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: leinfelder $'
7
 *     '$Date: 2011-11-11 09:54:49 -0800 (Fri, 11 Nov 2011) $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.restservice;
24

    
25
import java.io.ByteArrayInputStream;
26
import java.io.File;
27
import java.io.FileInputStream;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.util.Date;
32
import java.util.Enumeration;
33
import java.util.Map;
34

    
35
import javax.servlet.ServletContext;
36
import javax.servlet.http.HttpServletRequest;
37
import javax.servlet.http.HttpServletResponse;
38
import javax.xml.parsers.ParserConfigurationException;
39

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

    
79
import edu.ucsb.nceas.metacat.dataone.CNodeService;
80
import edu.ucsb.nceas.metacat.properties.PropertyService;
81
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
82

    
83
/**
84
 * CN REST service implementation handler
85
 * 
86
 * ******************
87
 	CNCore -- DONE
88
		create() - POST /d1/cn/object/PID
89
		listFormats() - GET /d1/cn/formats
90
		getFormat() - GET /d1/cn/formats/FMTID
91
		getLogRecords - GET /d1/cn/log
92
		reserveIdentifier() - POST /d1/cn/reserve
93
		listNodes() - Not implemented
94
		registerSystemMetadata() - POST /d1/meta/PID
95
	
96
	CNRead -- DONE
97
		get() - GET /d1/cn/object/PID
98
		getSystemMetadata() - GET /d1/cn/meta/PID
99
		resolve() - GET /d1/cn/resolve/PID
100
		assertRelation() - GET /d1/cn/assertRelation/PID
101
		getChecksum() - GET /d1/cn/checksum
102
		search() - Not implemented in Metacat
103
	
104
	CNAuthorization
105
		setOwner() - PUT /d1/cn/owner/PID
106
		isAuthorized() - GET /d1/cn/isAuthorized/PID
107
		setAccessPolicy() - POST /d1/cn/accessRules
108
		
109
	CNIdentity - not implemented at all on Metacat
110
	
111
	CNReplication
112
		setReplicationStatus() - PUT /replicaNotifications/PID
113
		updateReplicationMetadata() - PUT /replicaMetadata/PID
114
		setReplicationPolicy() - PUT /replicaPolicies/PID
115
		isNodeAuthorized() - GET /replicaAuthorizations/PID
116
	
117
	CNRegister -- not implemented at all in Metacat
118
 * ******************
119
 * @author leinfelder
120
 *
121
 */
122
public class CNResourceHandler extends D1ResourceHandler {
123

    
124
	/** CN-specific operations **/
125
    protected static final String RESOURCE_RESERVE = "reserve";
126
    protected static final String RESOURCE_FORMATS = "formats";
127
    protected static final String RESOURCE_RESOLVE = "resolve";
128
    protected static final String RESOURCE_ASSERT_RELATION = "assertRelation";
129
    protected static final String RESOURCE_OWNER = "owner";
130
    protected static final String RESOURCE_REPLICATION_POLICY = "replicaPolicies";
131
    protected static final String RESOURCE_REPLICATION_META = "replicaMetadata";
132
    protected static final String RESOURCE_REPLICATION_AUTHORIZED = "replicaAuthorizations";
133
    protected static final String RESOURCE_REPLICATION_NOTIFY = "replicaNotifications";
134
	
135
    public CNResourceHandler(ServletContext servletContext,
136
			HttpServletRequest request, HttpServletResponse response) {
137
		super(servletContext, request, response);
138
        logMetacat = Logger.getLogger(CNResourceHandler.class);
139
	}
140

    
141
	/**
142
     * This function is called from REST API servlet and handles each request to the servlet 
143
     * 
144
     * @param httpVerb (GET, POST, PUT or DELETE)
145
     */
146
    @Override
147
    public void handle(byte httpVerb) {
148
    	// prepare the handler
149
    	super.handle(httpVerb);
150
    	
151
        try {
152

    
153
        	// get the resource
154
            String resource = request.getPathInfo();
155
            resource = resource.substring(resource.indexOf("/") + 1);
156
                        
157
            // for the rest of the resouce
158
            String extra = null;
159
            
160
            logMetacat.debug("handling verb " + httpVerb + " request with resource '" + resource + "'");
161
            boolean status = false;
162

    
163
            if (resource != null) {
164

    
165
                if (resource.startsWith(RESOURCE_ACCESS_RULES) && httpVerb == PUT) {
166
                    logMetacat.debug("Setting access policy");
167
                    // after the command
168
                    extra = parseTrailing(resource, RESOURCE_ACCESS_RULES);
169
                    setAccess(extra);
170
                    status = true;
171
                    logMetacat.debug("done setting access");
172
                    
173
                } else if (resource.startsWith(RESOURCE_META)) {
174
                    logMetacat.debug("Using resource: " + RESOURCE_META);
175
                    
176
                    // after the command
177
                    extra = parseTrailing(resource, RESOURCE_META);
178
                    
179
                    // get
180
                    if (httpVerb == GET) {
181
                        getSystemMetadataObject(extra);
182
                        status = true;
183
                    }
184
                    // post to register system metadata
185
                    if (httpVerb == POST) {
186
                    	registerSystemMetadata(extra);
187
                    	status = true;
188
                    }
189

    
190
                } else if (resource.startsWith(RESOURCE_RESERVE)) {
191
                    // reserve the ID (in params)
192
                    if (httpVerb == POST) {
193
                    	reserve();
194
                    	status = true;
195
                    }
196
                } else if (resource.startsWith(RESOURCE_ASSERT_RELATION)) {
197
                	
198
                	// after the command
199
                    extra = parseTrailing(resource, RESOURCE_ASSERT_RELATION);
200
                    
201
                    // reserve the ID (in params)
202
                    if (httpVerb == GET) {
203
                    	assertRelation(extra);
204
                    	status = true;
205
                    }    
206
                } else if (resource.startsWith(RESOURCE_RESOLVE)) {
207
                	
208
                	// after the command
209
                    extra = parseTrailing(resource, RESOURCE_RESOLVE);
210
                    
211
                    // resolve the object location
212
                    if (httpVerb == GET) {
213
                    	resolve(extra);
214
                    	status = true;
215
                    }
216
                } else if (resource.startsWith(RESOURCE_OWNER)) {
217
                	
218
                	// after the command
219
                    extra = parseTrailing(resource, RESOURCE_OWNER);
220
                    
221
                    // set the owner
222
                    if (httpVerb == PUT) {
223
                    	owner(extra);
224
                    	status = true;
225
                    }    
226
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
227
                	
228
                	// after the command
229
                    extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
230
                    
231
                    // authorized?
232
                    if (httpVerb == GET) {
233
                    	isAuthorized(extra);
234
                    	status = true;
235
                    }    
236
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
237
                    logMetacat.debug("Using resource 'object'");
238
                    logMetacat.debug("D1 Rest: Starting resource processing...");
239
                    
240
                    // after the command
241
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
242
                    
243
                    logMetacat.debug("objectId: " + extra);
244
                    logMetacat.debug("verb:" + httpVerb);
245

    
246
                    if (httpVerb == GET) {
247
                    	if (extra != null) {
248
                    		getObject(extra);
249
                    	} else {
250
                    		listObjects();
251
                    	}
252
                        status = true;
253
                    } else if (httpVerb == POST) {
254
                        putObject(extra, FUNCTION_NAME_INSERT);
255
                        status = true;
256
                    }
257
                    
258
                } else if (resource.startsWith(RESOURCE_FORMATS)) {
259
                  logMetacat.debug("Using resource: " + RESOURCE_FORMATS);
260
                  
261
                  // after the command
262
                  extra = parseTrailing(resource, RESOURCE_FORMATS);
263
                  
264
                  // handle each verb
265
                  if (httpVerb == GET) {
266
                  	if (extra == null) {
267
                  		// list the formats collection
268
                  		listFormats();
269
                  	} else {
270
                  		// get the specified format
271
                  		getFormat(extra);
272
                  	}
273
                  	status = true;
274
                  }
275
                  
276
                } else if (resource.startsWith(RESOURCE_LOG)) {
277
                    logMetacat.debug("Using resource: " + RESOURCE_LOG);
278
                    //handle log events
279
                    if (httpVerb == GET) {
280
                        getLog();
281
                        status = true;
282
                    }
283

    
284
                } else if (resource.startsWith(RESOURCE_CHECKSUM)) {
285
                    logMetacat.debug("Using resource: " + RESOURCE_CHECKSUM);
286
                    
287
                    // after the command
288
                    extra = parseTrailing(resource, RESOURCE_CHECKSUM);
289
                    
290
                    //handle checksum requests
291
                    if (httpVerb == GET) {
292
                    
293
                        checksum(extra);
294
                        status = true;
295
                        
296
                    }
297
                
298
                } else if ( resource.startsWith(RESOURCE_REPLICATION_POLICY) && 
299
                        httpVerb == PUT) {
300
                    
301
                    logMetacat.debug("Using resource: " + RESOURCE_REPLICATION_POLICY);
302
                    // get the trailing pid
303
                    extra = parseTrailing(resource, RESOURCE_REPLICATION_POLICY);
304
                    setReplicationPolicy(extra);
305
                    status = true;
306

    
307
                } else if ( resource.startsWith(RESOURCE_REPLICATION_META) && 
308
                        httpVerb == PUT) {
309
                    
310
                    logMetacat.debug("Using resource: " + RESOURCE_REPLICATION_META);
311
                    // get the trailing pid
312
                    extra = parseTrailing(resource, RESOURCE_REPLICATION_META);
313
                    updateReplicationMetadata(extra);
314
                    status = true;
315

    
316
                } else if ( resource.startsWith(RESOURCE_REPLICATION_NOTIFY) && 
317
                        httpVerb == PUT ) {
318
                    
319
                    logMetacat.debug("Using resource: " + RESOURCE_REPLICATION_NOTIFY);
320
                    // get the trailing pid
321
                    extra = parseTrailing(resource, RESOURCE_REPLICATION_NOTIFY);
322
                    setReplicationStatus(extra);
323
                    status = true;
324
                    
325
                } else if ( resource.startsWith(RESOURCE_REPLICATION_AUTHORIZED) 
326
                        && httpVerb == GET) {
327
                    
328
                    logMetacat.debug("Using resource: " + RESOURCE_REPLICATION_AUTHORIZED);
329
                    // get the trailing pid
330
                    extra = parseTrailing(resource, RESOURCE_REPLICATION_AUTHORIZED);
331
                    isNodeAuthorized(extra);
332
                    status = true;
333
                    
334
                } 
335
                    
336
                if (!status) {
337
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
338
                }
339
            } else {
340
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
341
            }
342
        } catch (BaseException be) {
343
        	// report Exceptions as clearly and generically as possible
344
        	OutputStream out = null;
345
			try {
346
				out = response.getOutputStream();
347
			} catch (IOException ioe) {
348
				logMetacat.error("Could not get output stream from response", ioe);
349
			}
350
            serializeException(be, out);
351
        } catch (Exception e) {
352
            // report Exceptions as clearly and generically as possible
353
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
354
        	OutputStream out = null;
355
			try {
356
				out = response.getOutputStream();
357
			} catch (IOException ioe) {
358
				logMetacat.error("Could not get output stream from response", ioe);
359
			}
360
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
361
            serializeException(se, out);
362
        }
363
    }
364
    
365
    
366
    /**
367
     * Get the checksum for the given guid
368
     * 
369
     * @param guid
370
     * @throws NotImplemented 
371
     * @throws InvalidRequest 
372
     * @throws NotFound 
373
     * @throws NotAuthorized 
374
     * @throws ServiceFailure 
375
     * @throws InvalidToken 
376
     * @throws IOException 
377
     * @throws JiBXException 
378
     */
379
    private void checksum(String guid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, JiBXException, IOException {
380
    	Identifier guidid = new Identifier();
381
        guidid.setValue(guid);
382
        logMetacat.debug("getting checksum for object " + guid);
383
        Checksum c = CNodeService.getInstance(request).getChecksum(session, guidid);
384
        logMetacat.debug("got checksum " + c.getValue());
385
        response.setStatus(200);
386
        logMetacat.debug("serializing response");
387
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
388
        logMetacat.debug("done serializing response.");
389
        
390
    }
391
    
392
	/**
393
     * get the logs based on passed params.  Available 
394
     * params are token, fromDate, toDate, event.  See 
395
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
396
     * for more info
397
	 * @throws NotImplemented 
398
	 * @throws InvalidRequest 
399
	 * @throws NotAuthorized 
400
	 * @throws ServiceFailure 
401
	 * @throws InvalidToken 
402
	 * @throws IOException 
403
	 * @throws JiBXException 
404
     */
405
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
406
    {
407
        
408
        Date fromDate = null;
409
        Date toDate = null;
410
        Event event = null;
411
        Integer start = null;
412
        Integer count = null;
413
        
414
        try {
415
        	String fromDateS = params.get("fromDate")[0];
416
            logMetacat.debug("param fromDateS: " + fromDateS);
417
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
418
        } catch (Exception e) {
419
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
420
        }
421
        try {
422
        	String toDateS = params.get("toDate")[0];
423
            logMetacat.debug("param toDateS: " + toDateS);
424
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
425
        } catch (Exception e) {
426
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
427
		}
428
        try {
429
        	String eventS = params.get("event")[0];
430
            event = Event.convert(eventS);
431
        } catch (Exception e) {
432
        	logMetacat.warn("Could not parse event: " + e.getMessage());
433
		}
434
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
435
        
436
        try {
437
        	start =  Integer.parseInt(params.get("start")[0]);
438
        } catch (Exception e) {
439
			logMetacat.warn("Could not parse start: " + e.getMessage());
440
		}
441
        try {
442
        	count =  Integer.parseInt(params.get("count")[0]);
443
        } catch (Exception e) {
444
			logMetacat.warn("Could not parse count: " + e.getMessage());
445
		}
446
        
447
        logMetacat.debug("calling getLogRecords");
448
        Log log = CNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, start, count);
449
        
450
        OutputStream out = response.getOutputStream();
451
        response.setStatus(200);
452
        response.setContentType("text/xml");
453
        
454
        TypeMarshaller.marshalTypeToOutputStream(log, out);
455
  
456
    }
457

    
458
    /**
459
     * Implements REST version of DataONE CRUD API --> get
460
     * @param guid ID of data object to be read
461
     * @throws NotImplemented 
462
     * @throws InvalidRequest 
463
     * @throws NotFound 
464
     * @throws NotAuthorized 
465
     * @throws ServiceFailure 
466
     * @throws InvalidToken 
467
     * @throws IOException 
468
     */
469
    protected void getObject(String guid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException {
470

    
471
        Identifier id = new Identifier();
472
        id.setValue(guid);
473
            
474
        SystemMetadata sm = CNodeService.getInstance(request).getSystemMetadata(session, id);
475
        
476
        //set the content type
477
        if(sm.getFormatId().getValue().trim().equals(
478
        		ObjectFormatCache.getInstance().getFormat("text/csv").getFormatId().getValue()))
479
        {
480
            response.setContentType("text/csv");
481
            response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".csv");
482
        }
483
        else if(sm.getFormatId().getValue().trim().equals(
484
        		ObjectFormatCache.getInstance().getFormat("text/plain").getFormatId().getValue()))
485
        {
486
            response.setContentType("text/plain");
487
            response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".txt");
488
        } 
489
        else if(sm.getFormatId().getValue().trim().equals(
490
        		ObjectFormatCache.getInstance().getFormat("application/octet-stream").getFormatId().getValue()))
491
        {
492
            response.setContentType("application/octet-stream");
493
        }
494
        else
495
        {
496
            response.setContentType("text/xml");
497
            response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".xml");
498
        }
499
        
500
        InputStream data = CNodeService.getInstance(request).get(session, id);
501
        
502
        OutputStream out = response.getOutputStream();
503
        response.setStatus(200);
504
        IOUtils.copyLarge(data, out);
505
            
506
    }
507
    
508

    
509
    /**
510
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
511
     * @param guid ID of data object to be read
512
     * @throws NotImplemented 
513
     * @throws InvalidRequest 
514
     * @throws NotFound 
515
     * @throws NotAuthorized 
516
     * @throws ServiceFailure 
517
     * @throws InvalidToken 
518
     * @throws IOException 
519
     * @throws JiBXException 
520
     */
521
    protected void getSystemMetadataObject(String guid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
522

    
523
        Identifier id = new Identifier();
524
        id.setValue(guid);
525
        SystemMetadata sysmeta = CNodeService.getInstance(request).getSystemMetadata(session, id);
526
        
527
        response.setContentType("text/xml");
528
        response.setStatus(200);
529
        OutputStream out = response.getOutputStream();
530
        
531
        // Serialize and write it to the output stream
532
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
533
   }
534
    
535
    /**
536
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
537
     * 
538
     * @param guid - ID of data object to be inserted or updated.  If action is update, the pid
539
     *               is the existing pid.  If insert, the pid is the new one
540
     * @throws InvalidRequest 
541
     * @throws ServiceFailure 
542
     * @throws IdentifierNotUnique 
543
     * @throws JiBXException 
544
     * @throws NotImplemented 
545
     * @throws InvalidSystemMetadata 
546
     * @throws InsufficientResources 
547
     * @throws UnsupportedType 
548
     * @throws NotAuthorized 
549
     * @throws InvalidToken 
550
     * @throws IOException 
551
     * @throws IllegalAccessException 
552
     * @throws InstantiationException 
553
     */
554
    protected void putObject(String pid, String action) throws ServiceFailure, InvalidRequest, IdentifierNotUnique, JiBXException, InvalidToken, NotAuthorized, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, IOException, InstantiationException, IllegalAccessException {
555
        logMetacat.debug("Entering putObject: " + pid + "/" + action);
556
        
557
        // Read the incoming data from its Mime Multipart encoding
558
    	Map<String, File> files = collectMultipartFiles();
559
        InputStream object = null;
560
        InputStream sysmeta = null;
561

    
562
        File smFile = files.get("sysmeta");
563
        sysmeta = new FileInputStream(smFile);
564
        File objFile = files.get("object");
565
        object = new FileInputStream(objFile);
566
       
567
        if (action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
568

    
569
            logMetacat.debug("Commence creation...");
570
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
571

    
572
            Identifier id = new Identifier();
573
            id.setValue(pid);
574
            logMetacat.debug("creating object with pid " + id.getValue());
575
            Identifier rId = CNodeService.getInstance(request).create(session, id, object, smd);
576
            
577
            OutputStream out = response.getOutputStream();
578
            response.setStatus(200);
579
            response.setContentType("text/xml");
580
            
581
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
582
            
583
        } else {
584
            throw new InvalidRequest("1000", "Operation must be create.");
585
        }
586
    }
587

    
588
    /**
589
     * List the object formats registered with the system
590
     * @throws NotImplemented 
591
     * @throws InsufficientResources 
592
     * @throws NotFound 
593
     * @throws ServiceFailure 
594
     * @throws InvalidRequest 
595
     * @throws IOException 
596
     * @throws JiBXException 
597
     */
598
	private void listFormats() throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, NotImplemented, IOException, JiBXException {
599
      logMetacat.debug("Entering listFormats()");
600

    
601
      ObjectFormatList objectFormatList = CNodeService.getInstance(request).listFormats();
602
      // get the response output stream
603
      OutputStream out = response.getOutputStream();
604
      response.setStatus(200);
605
      response.setContentType("text/xml");
606
      
607
      // style the object with a processing directive
608
      String stylesheet = null;
609
      try {
610
    	  stylesheet = PropertyService.getProperty("dataone.types.xsl");
611
      } catch (PropertyNotFoundException e) {
612
    	  logMetacat.warn("Could not locate DataONE types XSLT: " + e.getMessage());
613
      }
614
      
615
      TypeMarshaller.marshalTypeToOutputStream(objectFormatList, out, stylesheet);
616
            
617
    }
618

    
619
		/**
620
     * Return the requested object format
621
     * 
622
     * @param fmtidStr the requested format identifier as a string
623
		 * @throws NotImplemented 
624
		 * @throws InsufficientResources 
625
		 * @throws NotFound 
626
		 * @throws ServiceFailure 
627
		 * @throws InvalidRequest 
628
		 * @throws IOException 
629
		 * @throws JiBXException 
630
     */
631
    private void getFormat(String fmtidStr) throws InvalidRequest, ServiceFailure, NotFound, InsufficientResources, NotImplemented, IOException, JiBXException {
632
      logMetacat.debug("Entering listFormats()");
633
      
634
      ObjectFormatIdentifier fmtid = new ObjectFormatIdentifier();
635
      fmtid.setValue(fmtidStr);
636
      
637
	  // get the specified object format
638
      ObjectFormat objectFormat = CNodeService.getInstance(request).getFormat(fmtid);
639
      
640
      OutputStream out = response.getOutputStream();
641
      response.setStatus(200);
642
      response.setContentType("text/xml");
643
      
644
      TypeMarshaller.marshalTypeToOutputStream(objectFormat, out);
645
      
646
    }
647
    
648
    /**
649
     * Reserve the given Identifier
650
     * @throws InvalidToken
651
     * @throws ServiceFailure
652
     * @throws NotAuthorized
653
     * @throws IdentifierNotUnique
654
     * @throws NotImplemented
655
     * @throws InvalidRequest
656
     * @throws IOException 
657
     * @throws JiBXException 
658
     */
659
    private void reserve() throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, NotImplemented, InvalidRequest, IOException, JiBXException {
660
		Identifier pid = null;
661
		String scope = null;
662
    	String format = null;
663
    	// gather the params
664
		try {
665
	    	String id = params.get("pid")[0];
666
			pid = new Identifier();
667
			pid.setValue(id);
668
		} catch (Exception e) {
669
			logMetacat.warn("pid not specified");
670
		}
671
		try {
672
			scope = params.get("scope")[0];
673
		} catch (Exception e) {
674
			logMetacat.warn("pid not specified");
675
		}
676
		try {
677
			format = params.get("format")[0];
678
		} catch (Exception e) {
679
			logMetacat.warn("pid not specified");
680
		} 
681
		// call the implementation
682
		Identifier resultPid = CNodeService.getInstance(request).reserveIdentifier(session, pid);
683
		OutputStream out = response.getOutputStream();
684
		response.setStatus(200);
685
		response.setContentType("text/xml");
686
		// send back the reserved pid
687
		TypeMarshaller.marshalTypeToOutputStream(resultPid, out);
688
    }
689
    
690
    /**
691
     * 
692
     * @param id
693
     * @throws InvalidRequest
694
     * @throws InvalidToken
695
     * @throws ServiceFailure
696
     * @throws NotAuthorized
697
     * @throws NotFound
698
     * @throws NotImplemented
699
     * @throws IOException
700
     * @throws JiBXException 
701
     */
702
    private void resolve(String id) throws InvalidRequest, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
703
		Identifier pid = new Identifier();
704
		pid.setValue(id);
705
		ObjectLocationList locationList = CNodeService.getInstance(request).resolve(session, pid);
706
	    OutputStream out = response.getOutputStream();
707
		response.setStatus(200);
708
		response.setContentType("text/xml");
709
		TypeMarshaller.marshalTypeToOutputStream(locationList, out);
710
		
711
    }
712

    
713
    /**
714
     * Assert that a relationship exists between two resources
715
     * @param id
716
     * @return
717
     * @throws InvalidToken
718
     * @throws ServiceFailure
719
     * @throws NotAuthorized
720
     * @throws NotFound
721
     * @throws InvalidRequest
722
     * @throws NotImplemented
723
     */
724
    private boolean assertRelation(String id) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented {
725
		Identifier pidOfSubject = new Identifier();
726
		pidOfSubject.setValue(id);
727
		String relationship = null;
728
		try {
729
			relationship = params.get("relationship")[0];
730
		} catch (Exception e) {
731
			logMetacat.warn("relationship not specified");
732
		}
733
		Identifier pidOfObject = new Identifier();
734
		try {
735
			String objPid = params.get("pidOfObject")[0];
736
			pidOfObject.setValue(objPid);
737
		} catch (Exception e) {
738
			logMetacat.warn("pidOfObject not specified");
739
		}
740
		boolean result = CNodeService.getInstance(request).assertRelation(session, pidOfSubject, relationship, pidOfObject);
741
		response.setStatus(200);
742
		response.setContentType("text/xml");
743
		return result;
744
    }
745
    
746
    /**
747
     * Set the owner of a resource
748
     * @param id
749
     * @throws JiBXException
750
     * @throws InvalidToken
751
     * @throws ServiceFailure
752
     * @throws NotFound
753
     * @throws NotAuthorized
754
     * @throws NotImplemented
755
     * @throws InvalidRequest
756
     * @throws IOException
757
     * @throws IllegalAccessException 
758
     * @throws InstantiationException 
759
     */
760
    private void owner(String id) 
761
        throws JiBXException, InvalidToken, ServiceFailure, 
762
        NotFound, NotAuthorized, NotImplemented, InvalidRequest, IOException, 
763
        InstantiationException, IllegalAccessException {
764
		
765
        Identifier pid = new Identifier();
766
		    pid.setValue(id);
767

    
768
        long serialVersion = 0L;
769
        String serialVersionStr = null;
770
        
771
        // get the serialVersion
772
        try {
773
            serialVersionStr = params.get("serialVersion")[0];
774
            serialVersion = new Long(serialVersionStr).longValue();
775
            
776
        } catch (NullPointerException e) {
777
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
778
            logMetacat.error(msg);
779
            throw new InvalidRequest("4442", msg);
780
            
781
        }		    
782
		    
783
		    // get the subject
784
		    String subjectStr = params.get("subject")[0];
785
		    Subject subject = TypeMarshaller.unmarshalTypeFromStream(Subject.class, new ByteArrayInputStream(subjectStr.getBytes("UTF-8")));
786
		    
787
		    Identifier retPid = CNodeService.getInstance(request).setOwner(session, pid, subject, serialVersion);
788
		    OutputStream out = response.getOutputStream();
789
		    response.setStatus(200);
790
		    response.setContentType("text/xml");
791
		    TypeMarshaller.marshalTypeToOutputStream(retPid, out);		
792
    }
793
    
794
    /**
795
     * Processes the authorization check for given id
796
     * @param id
797
     * @return
798
     * @throws ServiceFailure
799
     * @throws InvalidToken
800
     * @throws NotFound
801
     * @throws NotAuthorized
802
     * @throws NotImplemented
803
     * @throws InvalidRequest
804
     */
805
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
806
		Identifier pid = new Identifier();
807
		pid.setValue(id);
808
		String permission = params.get("action")[0];
809
		boolean result = CNodeService.getInstance(request).isAuthorized(session, pid, Permission.convert(permission));
810
		response.setStatus(200);
811
		response.setContentType("text/xml");
812
		return result;
813
    }
814
    
815
    /**
816
     * Register System Metadata without data or metadata object
817
     * @param pid identifier for System Metadata entry
818
     * @throws JiBXException 
819
     * @throws FileUploadException 
820
     * @throws IOException 
821
     * @throws InvalidRequest 
822
     * @throws ServiceFailure 
823
     * @throws InvalidSystemMetadata 
824
     * @throws NotAuthorized 
825
     * @throws NotImplemented 
826
     * @throws IllegalAccessException 
827
     * @throws InstantiationException 
828
     */
829
    protected Identifier registerSystemMetadata(String pid) throws ServiceFailure, InvalidRequest, IOException, FileUploadException, JiBXException, NotImplemented, NotAuthorized, InvalidSystemMetadata, InstantiationException, IllegalAccessException {
830
		logMetacat.debug("Entering registerSystemMetadata: " + pid);
831

    
832
		// get the system metadata from the request
833
		SystemMetadata systemMetadata = collectSystemMetadata();
834

    
835
		Identifier guid = new Identifier();
836
		guid.setValue(pid);
837
		logMetacat.debug("registering system metadata with pid " + guid.getValue());
838
		Identifier retGuid = CNodeService.getInstance(request).registerSystemMetadata(session, guid, systemMetadata);
839
		
840
		response.setStatus(200);
841
		response.setContentType("text/xml");
842
		return retGuid;
843
			
844
	}
845
    
846
    /**
847
     * set the access perms on a document
848
     * @throws JiBXException 
849
     * @throws InvalidRequest 
850
     * @throws NotImplemented 
851
     * @throws NotAuthorized 
852
     * @throws NotFound 
853
     * @throws ServiceFailure 
854
     * @throws InvalidToken 
855
     * @throws IllegalAccessException 
856
     * @throws InstantiationException 
857
     * @throws IOException 
858
     * @throws SAXException 
859
     * @throws ParserConfigurationException 
860
     */
861
    protected void setAccess(String pid) 
862
        throws JiBXException, InvalidToken, ServiceFailure, NotFound, 
863
        NotAuthorized, NotImplemented, InvalidRequest, IOException, 
864
        InstantiationException, IllegalAccessException, ParserConfigurationException, 
865
        SAXException {
866

    
867
        long serialVersion = 0L;
868
        String serialVersionStr = null;
869
        
870
        // get the serialVersion
871
        try {
872
            serialVersionStr = params.get("serialVersion")[0];
873
            serialVersion = new Long(serialVersionStr).longValue();
874
            
875
        } catch (NullPointerException e) {
876
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
877
            logMetacat.error(msg);
878
            throw new InvalidRequest("4402", msg);
879
            
880
        }
881
        
882
        Identifier id = new Identifier();
883
        id.setValue(pid);
884
        
885
        AccessPolicy accessPolicy = collectAccessPolicy();
886
        CNodeService.getInstance(request).setAccessPolicy(session, id, accessPolicy, serialVersion);
887

    
888
    }
889
    
890
    /**
891
     *	List the objects
892
     *
893
     * @throws NotImplemented 
894
     * @throws InvalidRequest 
895
     * @throws NotAuthorized 
896
     * @throws ServiceFailure 
897
     * @throws InvalidToken 
898
     * @throws NotFound 
899
     * @throws IOException 
900
     * @throws JiBXException
901
     * @throws Exception
902
     */
903
    private void listObjects() throws InvalidToken, ServiceFailure, NotAuthorized,
904
			InvalidRequest, NotImplemented, NotFound, IOException,
905
			JiBXException {
906

    
907
		Date startTime = null;
908
		Date endTime = null;
909
		ObjectFormat objectFormat = null;
910
		boolean replicaStatus = false;
911
		int start = 0;
912
		int count = -1;
913
		Enumeration<String> paramlist = request.getParameterNames();
914
		while (paramlist.hasMoreElements()) {
915
			// parse the params and make the call
916
			String name = paramlist.nextElement();
917
			String[] value = request.getParameterValues(name);
918

    
919
			if (name.equals("startTime") && value != null) {
920
				try {
921
					startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
922
				} catch (Exception e) { 
923
					// if we can't parse it, just don't use the startTime param
924
					logMetacat.warn("Could not parse startTime: " + value[0]);
925
					startTime = null;
926
				}
927
			} else if (name.equals("endTime") && value != null) {
928
				try {
929
					endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
930
				} catch (Exception e) { 
931
					// if we can't parse it, just don't use the endTime param
932
					logMetacat.warn("Could not parse endTime: " + value[0]);
933
					endTime = null;
934
				}
935
			} else if (name.equals("objectFormat") && value != null) {
936
				objectFormat = ObjectFormatCache.getInstance().getFormat(value[0]);
937
			} else if (name.equals("replicaStatus") && value != null) {
938
				replicaStatus = Boolean.parseBoolean(value[0]);
939
			} else if (name.equals("start") && value != null) {
940
				start = Integer.valueOf(value[0]);
941
			} else if (name.equals("count") && value != null) {
942
				count = Integer.valueOf(value[0]);
943
			}
944
		}
945
		// make the call
946
		logMetacat.debug("session: " + session + " startTime: " + startTime
947
				+ " endtime: " + endTime + " objectFormat: " + objectFormat
948
				+ " replicaStatus: " + replicaStatus + " start: " + start
949
				+ " count: " + count);
950

    
951
		ObjectFormatIdentifier fmtid = null;
952
		if (objectFormat != null) {
953
			fmtid = objectFormat.getFormatId();
954
		}
955
		
956
		// get the list
957
		ObjectList ol = 
958
			CNodeService.getInstance(request).listObjects(session,
959
				startTime, endTime, fmtid, replicaStatus, start, count);
960

    
961
		// send it
962
		OutputStream out = response.getOutputStream();
963
		response.setStatus(200);
964
		response.setContentType("text/xml");
965
		
966
		// style the object with a processing directive
967
		String stylesheet = null;
968
		try {
969
			stylesheet = PropertyService.getProperty("dataone.types.xsl");
970
		} catch (PropertyNotFoundException e) {
971
			logMetacat.warn("Could not locate DataONE types XSLT: " + e.getMessage());
972
		}
973
	      
974
		// Serialize and write it to the output stream
975
		TypeMarshaller.marshalTypeToOutputStream(ol, out, stylesheet);
976
	}
977
    
978
    /**
979
     * Pass the request to get node replication authorization to CNodeService
980
     * 
981
     * @param pid  the identifier of the object to get authorization to replicate
982
     * 
983
     * @throws NotImplemented
984
     * @throws NotAuthorized
985
     * @throws InvalidToken
986
     * @throws ServiceFailure
987
     * @throws NotFound
988
     * @throws InvalidRequest
989
     */
990
    public boolean isNodeAuthorized(String pid) 
991
        throws NotImplemented, NotAuthorized, InvalidToken, ServiceFailure, 
992
        NotFound, InvalidRequest {
993
        
994
        boolean result = false;
995
        Subject targetNodeSubject = new Subject();
996
        String nodeSubject = null;
997
        Permission permission = null;
998
        String replPermission = null;
999
        
1000
        // get the pid
1001
        Identifier identifier = new Identifier();
1002
        identifier.setValue(pid);
1003
        
1004
        // get the target node subject
1005
        try {
1006
            nodeSubject = params.get("targetNodeSubject")[0];
1007
            targetNodeSubject.setValue(nodeSubject);
1008
            
1009
        } catch (NullPointerException e) {
1010
            String msg = "The 'targetNodeSubject' must be provided as a parameter and was not.";
1011
            logMetacat.error(msg);
1012
            throw new InvalidRequest("4873", msg);
1013
            
1014
        }
1015
        
1016
        // get the permission
1017
        try {
1018
            replPermission = params.get("replicatePermission")[0];
1019
            permission = Permission.convert(replPermission);
1020

    
1021
        } catch (NullPointerException e) {
1022
            String msg = "The 'replicatePermission' must be provided as a parameter and was not.";
1023
            logMetacat.error(msg);
1024
            throw new InvalidRequest("4873", msg);
1025

    
1026
        }
1027
        
1028
        result =
1029
            CNodeService.getInstance(request).isNodeAuthorized(session, targetNodeSubject, identifier, permission);
1030
        
1031
        response.setStatus(200);
1032
        response.setContentType("text/xml");
1033
        return result;
1034
        
1035
    }
1036
    
1037
    /**
1038
     * Pass the request to set the replication policy to CNodeService
1039
     * 
1040
     * @param pid  the identifier of the object to set the replication policy on
1041
     * 
1042
     * @throws NotImplemented
1043
     * @throws NotFound
1044
     * @throws NotAuthorized
1045
     * @throws ServiceFailure
1046
     * @throws InvalidRequest
1047
     * @throws InvalidToken
1048
     * @throws IOException
1049
     * @throws InstantiationException
1050
     * @throws IllegalAccessException
1051
     * @throws JiBXException
1052
     */
1053
    public boolean setReplicationPolicy(String pid) 
1054
        throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
1055
        InvalidRequest, InvalidToken, IOException, InstantiationException, 
1056
        IllegalAccessException, JiBXException {
1057
        
1058
        boolean result = false;
1059
        ReplicationPolicy policy = null;
1060
        long serialVersion = 0L;
1061
        String serialVersionStr = null;
1062
       
1063
        Identifier identifier = new Identifier();
1064
        identifier.setValue(pid);
1065
        
1066
        policy = collectReplicationPolicy();
1067

    
1068
        // get the serialVersion
1069
        try {
1070
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1071
            serialVersion = new Long(serialVersionStr).longValue();
1072
            
1073
        } catch (NullPointerException e) {
1074
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1075
            logMetacat.error(msg);
1076
            throw new InvalidRequest("4883", msg);
1077
            
1078
        }
1079
        result = 
1080
            CNodeService.getInstance(request).setReplicationPolicy(session, identifier, policy, serialVersion);
1081
        response.setStatus(200);
1082
        response.setContentType("text/xml");
1083
        return result;
1084
        
1085
    }
1086
    
1087
    /**
1088
     * Pass the request to set the replication status to CNodeService
1089
     * 
1090
     * @param pid  the identifier of the object to set the replication status on
1091
     * 
1092
     * @throws ServiceFailure
1093
     * @throws NotImplemented
1094
     * @throws InvalidToken
1095
     * @throws NotAuthorized
1096
     * @throws InvalidRequest
1097
     * @throws NotFound
1098
     */
1099
    public boolean setReplicationStatus(String pid) 
1100
        throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
1101
        InvalidRequest, NotFound {
1102

    
1103
        File tmpDir = getTempDirectory();
1104
        MultipartRequest mr = null;
1105
        boolean result = false;
1106
        Identifier identifier = new Identifier();
1107
        identifier.setValue(pid);
1108
        long serialVersion = 0L;
1109
        String serialVersionStr = null;
1110
        ReplicationStatus status = null;
1111
        String replicationStatus = null;
1112
        NodeReference targetNodeRef = null;
1113
        String targetNode = null;
1114
        
1115
        // Parse the params out of the multipart form data
1116
        // Read the incoming data from its Mime Multipart encoding
1117
        logMetacat.debug("Parsing ReplicaStatus from the mime multipart entity");
1118

    
1119
        // handle MMP inputs
1120
        MultipartRequestResolver mrr = 
1121
            new MultipartRequestResolver(tmpDir.getAbsolutePath(),1000000000, 0);
1122
        
1123
        try {
1124
            mr = mrr.resolveMultipart(request);
1125
            multipartparams = mr.getMultipartParameters();
1126
            logMetacat.debug("Resolved the ReplicaStatus multipart request.");
1127
            
1128
        } catch (IOException e) {
1129
            throw new ServiceFailure("4852", "Couldn't resolve the multipart request: " +
1130
                e.getMessage());
1131
            
1132
        } catch (FileUploadException e) {
1133
            throw new ServiceFailure("4852", "Couldn't resolve the multipart request: " +
1134
                    e.getMessage());
1135
            
1136
        } catch (Exception e) {
1137
            throw new ServiceFailure("4852", "Couldn't resolve the multipart request: " +
1138
                    e.getMessage());
1139
            
1140
        }
1141

    
1142
        // get the serialVersion
1143
        try {
1144
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1145
            serialVersion = new Long(serialVersionStr).longValue();
1146
            
1147
        } catch (NullPointerException e) {
1148
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1149
            logMetacat.error(msg);
1150
            throw new InvalidRequest("4730", msg);
1151
            
1152
        }
1153
        
1154
        // get the replication status param
1155
        try {
1156
            replicationStatus = multipartparams.get("replicationStatus").get(0);
1157
            status = ReplicationStatus.convert(replicationStatus);
1158
        } catch (Exception e) {
1159
            // TODO Auto-generated catch block
1160
            e.printStackTrace();
1161
        }
1162
        
1163
        // get the target node reference param
1164
        targetNode = multipartparams.get("nodeRef").get(0);
1165
        targetNodeRef = new NodeReference();
1166
        targetNodeRef.setValue(targetNode);
1167
        
1168
        result = 
1169
            CNodeService.getInstance(request).setReplicationStatus(session, identifier, targetNodeRef, status, serialVersion);       
1170
        response.setStatus(200);
1171
        response.setContentType("text/xml");
1172
        return result;
1173
        
1174
    }
1175
    
1176
    /**
1177
     * Pass the request to update the replication metadata to CNodeService
1178
     * 
1179
     * @param pid  the identifier of the object to update the replication metadata on
1180
     * 
1181
     * @throws ServiceFailure
1182
     * @throws NotImplemented
1183
     * @throws InvalidToken
1184
     * @throws NotAuthorized
1185
     * @throws InvalidRequest
1186
     * @throws NotFound
1187
     */
1188
    public boolean updateReplicationMetadata(String pid) 
1189
        throws ServiceFailure, NotImplemented, InvalidToken, NotAuthorized, 
1190
        InvalidRequest, NotFound {
1191

    
1192
        boolean result = false;
1193
        long serialVersion = 0L;
1194
        String serialVersionStr = null;
1195
        Replica replica = null;
1196
        Identifier identifier = new Identifier();
1197
        identifier.setValue(pid);
1198

    
1199
        replica = collectReplicaMetadata();
1200
        
1201
        // get the serialVersion
1202
        try {
1203
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1204
            serialVersion = new Long(serialVersionStr).longValue();
1205
            
1206
        } catch (NullPointerException e) {
1207
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1208
            logMetacat.error(msg);
1209
            throw new InvalidRequest("4853", msg);
1210
            
1211
        }
1212

    
1213
        result = 
1214
            CNodeService.getInstance(request).updateReplicationMetadata(session, identifier, replica, serialVersion);
1215
        response.setStatus(200);
1216
        response.setContentType("text/xml");
1217
        return result;
1218

    
1219
    }
1220

    
1221
}
(1-1/9)