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: tao $'
7
 *     '$Date: 2015-03-09 18:29:27 -0700 (Mon, 09 Mar 2015) $'
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.v2;
24

    
25
import java.io.File;
26
import java.io.FileInputStream;
27
import java.io.FileNotFoundException;
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.v2.formats.ObjectFormatInfo;
44
import org.dataone.service.exceptions.BaseException;
45
import org.dataone.service.exceptions.IdentifierNotUnique;
46
import org.dataone.service.exceptions.InsufficientResources;
47
import org.dataone.service.exceptions.InvalidRequest;
48
import org.dataone.service.exceptions.InvalidSystemMetadata;
49
import org.dataone.service.exceptions.InvalidToken;
50
import org.dataone.service.exceptions.NotAuthorized;
51
import org.dataone.service.exceptions.NotFound;
52
import org.dataone.service.exceptions.NotImplemented;
53
import org.dataone.service.exceptions.ServiceFailure;
54
import org.dataone.service.exceptions.UnsupportedType;
55
import org.dataone.service.exceptions.VersionMismatch;
56
import org.dataone.service.types.v1.AccessPolicy;
57
import org.dataone.service.types.v1.Checksum;
58
import org.dataone.service.types.v1.ChecksumAlgorithmList;
59
import org.dataone.service.types.v1.DescribeResponse;
60
import org.dataone.service.types.v1.Event;
61
import org.dataone.service.types.v1.Identifier;
62
import org.dataone.service.types.v2.Log;
63
import org.dataone.service.types.v2.OptionList;
64
import org.dataone.service.types.v1.NodeReference;
65
import org.dataone.service.types.v2.ObjectFormat;
66
import org.dataone.service.types.v1.ObjectFormatIdentifier;
67
import org.dataone.service.types.v2.ObjectFormatList;
68
import org.dataone.service.types.v1.ObjectList;
69
import org.dataone.service.types.v1.ObjectLocationList;
70
import org.dataone.service.types.v1.Permission;
71
import org.dataone.service.types.v1.Replica;
72
import org.dataone.service.types.v1.ReplicationPolicy;
73
import org.dataone.service.types.v1.ReplicationStatus;
74
import org.dataone.service.types.v1.Subject;
75
import org.dataone.service.types.v2.SystemMetadata;
76
import org.dataone.service.util.Constants;
77
import org.dataone.service.util.DateTimeMarshaller;
78
import org.dataone.service.util.EncodingUtilities;
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.common.query.stream.ContentTypeInputStream;
85
import edu.ucsb.nceas.metacat.dataone.CNodeService;
86
import edu.ucsb.nceas.metacat.properties.PropertyService;
87
import edu.ucsb.nceas.metacat.restservice.D1ResourceHandler;
88
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
89

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

    
120
    /** CN-specific operations **/
121
    protected static final String RESOURCE_RESERVE = "reserve";
122
    protected static final String RESOURCE_FORMATS = "formats";
123
    protected static final String RESOURCE_RESOLVE = "resolve";
124
    protected static final String RESOURCE_OWNER = "owner";
125
    protected static final String RESOURCE_REPLICATION_POLICY = "replicaPolicies";
126
    protected static final String RESOURCE_REPLICATION_META = "replicaMetadata";
127
    protected static final String RESOURCE_REPLICATION_AUTHORIZED = "replicaAuthorizations";
128
    protected static final String RESOURCE_REPLICATION_NOTIFY = "replicaNotifications";
129

    
130
    public CNResourceHandler(ServletContext servletContext,
131
            HttpServletRequest request, HttpServletResponse response) {
132
        super(servletContext, request, response);
133
        logMetacat = Logger.getLogger(CNResourceHandler.class);
134
    }
135

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

    
148
        try {
149

    
150
        	// only service requests if we have D1 configured
151
        	if (!isD1Enabled()) {
152
        		ServiceFailure se = new ServiceFailure("0000", "DataONE services are not enabled on this node");
153
                serializeException(se, response.getOutputStream());
154
                return;
155
        	}
156
        	
157
            // get the resource
158
            String resource = request.getPathInfo();
159
            resource = resource.substring(resource.indexOf("/") + 1);
160

    
161
            // for the rest of the resouce
162
            String extra = null;
163

    
164
            logMetacat.debug("handling verb " + httpVerb
165
                    + " request with resource '" + resource + "'");
166
            boolean status = false;
167

    
168
            if (resource != null) {
169

    
170
                if (resource.startsWith(RESOURCE_ACCESS_RULES)
171
                        && httpVerb == PUT) {
172
                    logMetacat.debug("Setting access policy");
173
                    // after the command
174
                    extra = parseTrailing(resource, RESOURCE_ACCESS_RULES);
175
                    setAccess(extra);
176
                    status = true;
177
                    logMetacat.debug("done setting access");
178

    
179
                } else if (resource.startsWith(RESOURCE_META)) {
180
                    logMetacat.debug("Using resource: " + RESOURCE_META);
181

    
182
                    // after the command
183
                    extra = parseTrailing(resource, RESOURCE_META);
184

    
185
                    // get
186
                    if (httpVerb == GET) {
187
                        getSystemMetadataObject(extra);
188
                        status = true;
189
                    }
190
                    // post to register system metadata
191
                    if (httpVerb == POST) {
192
                        registerSystemMetadata();
193
                        status = true;
194
                    }
195
                    else if (httpVerb == PUT) {
196
                        logMetacat.debug("Using resource 'meta' for PUT");
197
                        updateSystemMetadata();
198
                        status = true;
199
                    }
200

    
201
                } else if (resource.startsWith(RESOURCE_RESERVE)) {
202
                    // reserve the ID (in params)
203
                    if (httpVerb == POST) {
204
                        reserve();
205
                        status = true;
206
                    }
207
                } else if (resource.startsWith(RESOURCE_RESOLVE)) {
208

    
209
                    // after the command
210
                    extra = parseTrailing(resource, RESOURCE_RESOLVE);
211

    
212
                    // resolve the object location
213
                    if (httpVerb == GET) {
214
                        resolve(extra);
215
                        status = true;
216
                    }
217
                } else if (resource.startsWith(RESOURCE_OWNER)) {
218

    
219
                    // after the command
220
                    extra = parseTrailing(resource, RESOURCE_OWNER);
221

    
222
                    // set the owner
223
                    if (httpVerb == PUT) {
224
                        owner(extra);
225
                        status = true;
226
                    }
227
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
228

    
229
                    // after the command
230
                    extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
231

    
232
                    // authorized?
233
                    if (httpVerb == GET) {
234
                        isAuthorized(extra);
235
                        status = true;
236
                    }
237
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
238
                    logMetacat.debug("Using resource 'object'");
239
                    logMetacat
240
                            .debug("D1 Rest: Starting resource processing...");
241

    
242
                    // after the command
243
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
244

    
245
                    logMetacat.debug("objectId: " + extra);
246
                    logMetacat.debug("verb:" + httpVerb);
247

    
248
                    if (httpVerb == GET) {
249
                        if (extra != null) {
250
                            getObject(extra);
251
                        } else {
252
                            listObjects();
253
                        }
254
                        status = true;
255
                    } else if (httpVerb == POST) {
256
                        putObject(FUNCTION_NAME_INSERT);
257
                        status = true;
258
                    } else if (httpVerb == HEAD) {
259
                        describeObject(extra);
260
                        status = true;
261
                    } else if (httpVerb == DELETE) {
262
                        deleteObject(extra);
263
                        status = true;
264
                    } 
265

    
266
                } else if (resource.startsWith(RESOURCE_FORMATS)) {
267
                    logMetacat.debug("Using resource: " + RESOURCE_FORMATS);
268

    
269
                    // after the command
270
                    extra = parseTrailing(resource, RESOURCE_FORMATS);
271

    
272
                    // handle each verb
273
                    if (httpVerb == GET) {
274
                        if (extra == null) {
275
                            // list the formats collection
276
                            listFormats();
277
                        } else {
278
                            // get the specified format
279
                            getFormat(extra);
280
                        }
281
                        status = true;
282
                    }
283

    
284
                } else if (resource.startsWith(RESOURCE_LOG)) {
285
                    logMetacat.debug("Using resource: " + RESOURCE_LOG);
286
                    // handle log events
287
                    if (httpVerb == GET) {
288
                        getLog();
289
                        status = true;
290
                    }
291

    
292
                } else if (resource.startsWith(Constants.RESOURCE_ARCHIVE)) {
293
                    logMetacat.debug("Using resource " + Constants.RESOURCE_ARCHIVE);
294
                    // handle archive events
295
                    if (httpVerb == PUT) {
296
                        extra = parseTrailing(resource, Constants.RESOURCE_ARCHIVE);
297
                        archive(extra);
298
                        status = true;
299
                    }
300
                } else if (resource.startsWith(Constants.RESOURCE_CHECKSUM)) {
301
                    logMetacat.debug("Using resource: " + Constants.RESOURCE_CHECKSUM);
302

    
303
                    // after the command
304
                    extra = parseTrailing(resource, Constants.RESOURCE_CHECKSUM);
305

    
306
                    // handle checksum requests
307
                    if (httpVerb == GET) {
308

    
309
                    	if (extra != null && extra.length() > 0) {
310
	                        checksum(extra);
311
	                        status = true;
312
                    	} else {
313
                    		listChecksumAlgorithms();
314
                    		status = true;
315
                    	}
316

    
317
                    }
318

    
319
                } else if (resource.startsWith(RESOURCE_REPLICATION_POLICY)
320
                        && httpVerb == PUT) {
321

    
322
                    logMetacat.debug("Using resource: "
323
                            + RESOURCE_REPLICATION_POLICY);
324
                    // get the trailing pid
325
                    extra = parseTrailing(resource, RESOURCE_REPLICATION_POLICY);
326
                    setReplicationPolicy(extra);
327
                    status = true;
328

    
329
                } else if (resource.startsWith(RESOURCE_REPLICATION_META)
330
                        && httpVerb == PUT) {
331

    
332
                    logMetacat.debug("Using resource: "
333
                            + RESOURCE_REPLICATION_META);
334
                    // get the trailing pid
335
                    extra = parseTrailing(resource, RESOURCE_REPLICATION_META);
336
                    updateReplicationMetadata(extra);
337
                    status = true;
338

    
339
                } else if (resource.startsWith(RESOURCE_REPLICATION_NOTIFY)
340
                        && httpVerb == PUT) {
341

    
342
                    logMetacat.debug("Using resource: "
343
                            + RESOURCE_REPLICATION_NOTIFY);
344
                    // get the trailing pid
345
                    extra = parseTrailing(resource, RESOURCE_REPLICATION_NOTIFY);
346
                    setReplicationStatus(extra);
347
                    status = true;
348

    
349
                } else if (resource.startsWith(RESOURCE_REPLICATION_AUTHORIZED)
350
                        && httpVerb == GET) {
351

    
352
                    logMetacat.debug("Using resource: "
353
                            + RESOURCE_REPLICATION_AUTHORIZED);
354
                    // get the trailing pid
355
                    extra = parseTrailing(resource,
356
                            RESOURCE_REPLICATION_AUTHORIZED);
357
                    isNodeAuthorized(extra);
358
                    status = true;
359

    
360
                } else if (resource.startsWith(Constants.RESOURCE_MONITOR_PING)) {
361
                    if (httpVerb == GET) {
362
                    	// after the command
363
                        extra = parseTrailing(resource, Constants.RESOURCE_MONITOR_PING);
364
                        
365
                        logMetacat.debug("processing ping request");
366
                        Date result = CNodeService.getInstance(request).ping();
367
                        // TODO: send to output	
368
                        status = true;
369
                    }
370
                } else if (resource.startsWith(Constants.RESOURCE_META_OBSOLETEDBY)
371
                        && httpVerb == PUT) {
372

    
373
                    logMetacat.debug("Using resource: "
374
                            + Constants.RESOURCE_META_OBSOLETEDBY);
375
                    // get the trailing pid
376
                    extra = parseTrailing(resource, Constants.RESOURCE_META_OBSOLETEDBY);
377
                    setObsoletedBy(extra);
378
                    status = true;
379
                } else if (resource.startsWith(Constants.RESOURCE_REPLICATION_DELETE_REPLICA)
380
                        && httpVerb == PUT) {
381

    
382
                    logMetacat.debug("Using resource: "
383
                            + Constants.RESOURCE_REPLICATION_DELETE_REPLICA);
384
                    // get the trailing pid
385
                    extra = parseTrailing(resource, Constants.RESOURCE_REPLICATION_DELETE_REPLICA);
386
                    deleteReplica(extra);
387
                    status = true;
388
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
389
                    logMetacat.debug("Using resource " + RESOURCE_VIEWS);
390
                    // after the command
391
                    extra = parseTrailing(resource, RESOURCE_VIEWS);
392
                    logMetacat.debug("view extra: " + extra);
393

    
394
                    String format = null;
395
                    String pid = null;
396

    
397
                    if (extra != null) {
398
                        // get the format
399
                        int formatIndex = extra.length();
400
                        if (extra.indexOf("/") > -1) {
401
                            formatIndex = extra.indexOf("/");
402
                        }
403
                        format = extra.substring(0, formatIndex);
404
                        logMetacat.debug("view format: " + format);
405
                        
406
                        // get the pid if it is there
407
                        pid = extra.substring(formatIndex, extra.length());
408
                        if (pid != null && pid.length() == 0) {
409
                            pid = null;
410
                        } else {
411
                            if (pid.startsWith("/")) {
412
                                pid = pid.substring(1);
413
                            }
414
                        }
415
                        logMetacat.debug("pid: " + pid);
416

    
417
                    }
418
                    logMetacat.debug("verb:" + httpVerb);
419
                    if (httpVerb == GET) {
420
                        doViews(format, pid);
421
                        status = true;
422
                    }
423
                } 
424

    
425
                if (!status) {
426
                    throw new ServiceFailure("0000", "Unknown error, status = "
427
                            + status);
428
                }
429
            } else {
430
                throw new InvalidRequest("0000", "No resource matched for "
431
                        + resource);
432
            }
433
        } catch (BaseException be) {
434
            // report Exceptions as clearly and generically as possible
435
            OutputStream out = null;
436
            try {
437
                out = response.getOutputStream();
438
            } catch (IOException ioe) {
439
                logMetacat.error("Could not get output stream from response",
440
                        ioe);
441
            }
442
            serializeException(be, out);
443
        } catch (Exception e) {
444
            // report Exceptions as clearly and generically as possible
445
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
446
            OutputStream out = null;
447
            try {
448
                out = response.getOutputStream();
449
            } catch (IOException ioe) {
450
                logMetacat.error("Could not get output stream from response",
451
                        ioe);
452
            }
453
            ServiceFailure se = new ServiceFailure("0000", e.getMessage());
454
            serializeException(se, out);
455
        }
456
    }
457

    
458
    /**
459
     * Get the checksum for the given guid
460
     * 
461
     * @param guid
462
     * @throws NotImplemented
463
     * @throws InvalidRequest
464
     * @throws NotFound
465
     * @throws NotAuthorized
466
     * @throws ServiceFailure
467
     * @throws InvalidToken
468
     * @throws IOException
469
     * @throws JiBXException
470
     */
471
    private void checksum(String guid) throws InvalidToken, ServiceFailure,
472
            NotAuthorized, NotFound, InvalidRequest, NotImplemented,
473
            JiBXException, IOException {
474
        Identifier guidid = new Identifier();
475
        guidid.setValue(guid);
476
        logMetacat.debug("getting checksum for object " + guid);
477
        Checksum c = CNodeService.getInstance(request).getChecksum(session,
478
                guidid);
479
        logMetacat.debug("got checksum " + c.getValue());
480
        response.setStatus(200);
481
        logMetacat.debug("serializing response");
482
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
483
        logMetacat.debug("done serializing response.");
484

    
485
    }
486

    
487
    /**
488
     * get the logs based on passed params. Available params are token,
489
     * fromDate, toDate, event. See
490
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud
491
     * .html#MN_crud.getLogRecords for more info
492
     * 
493
     * @throws NotImplemented
494
     * @throws InvalidRequest
495
     * @throws NotAuthorized
496
     * @throws ServiceFailure
497
     * @throws InvalidToken
498
     * @throws IOException
499
     * @throws JiBXException
500
     */
501
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized,
502
            InvalidRequest, NotImplemented, IOException, JiBXException {
503

    
504
        Date fromDate = null;
505
        Date toDate = null;
506
        String event = null;
507
        Integer start = null;
508
        Integer count = null;
509
        String pidFilter = null;
510

    
511
        try {
512
            String fromDateS = params.get("fromDate")[0];
513
            logMetacat.debug("param fromDateS: " + fromDateS);
514
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
515
        } catch (Exception e) {
516
            logMetacat.warn("Could not parse fromDate: " + e.getMessage());
517
        }
518
        try {
519
            String toDateS = params.get("toDate")[0];
520
            logMetacat.debug("param toDateS: " + toDateS);
521
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
522
        } catch (Exception e) {
523
            logMetacat.warn("Could not parse toDate: " + e.getMessage());
524
        }
525
        try {
526
            event = params.get("event")[0];
527
        } catch (Exception e) {
528
            logMetacat.warn("Could not parse event: " + e.getMessage());
529
        }
530
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
531

    
532
        try {
533
            start = Integer.parseInt(params.get("start")[0]);
534
        } catch (Exception e) {
535
            logMetacat.warn("Could not parse start: " + e.getMessage());
536
        }
537
        try {
538
            count = Integer.parseInt(params.get("count")[0]);
539
        } catch (Exception e) {
540
            logMetacat.warn("Could not parse count: " + e.getMessage());
541
        }
542

    
543
        try {
544
            pidFilter = params.get("pidFilter")[0];
545
        } catch (Exception e) {
546
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
547
        }
548
        
549
        logMetacat.debug("calling getLogRecords");
550
        Log log = CNodeService.getInstance(request).getLogRecords(session,
551
                fromDate, toDate, event, pidFilter, start, count);
552

    
553
        OutputStream out = response.getOutputStream();
554
        response.setStatus(200);
555
        response.setContentType("text/xml");
556

    
557
        TypeMarshaller.marshalTypeToOutputStream(log, out);
558

    
559
    }
560

    
561
    /**
562
     * Implements REST version of DataONE CRUD API --> get
563
     * 
564
     * @param guid
565
     *            ID of data object to be read
566
     * @throws NotImplemented
567
     * @throws InvalidRequest
568
     * @throws NotFound
569
     * @throws NotAuthorized
570
     * @throws ServiceFailure
571
     * @throws InvalidToken
572
     * @throws IOException
573
     */
574
    protected void getObject(String guid) throws InvalidToken, ServiceFailure,
575
            NotAuthorized, NotFound, InvalidRequest, NotImplemented,
576
            IOException {
577

    
578
        Identifier id = new Identifier();
579
        id.setValue(guid);
580

    
581
        SystemMetadata sm = CNodeService.getInstance(request)
582
                .getSystemMetadata(session, id);
583

    
584
        // set the headers for the content
585
        String mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
586
        if (mimeType == null) {
587
        	mimeType = "application/octet-stream";
588
        }
589
        String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
590
        String filename = id.getValue();
591
        if (extension != null) {
592
        	filename = id.getValue() + extension;
593
        }
594
        response.setContentType(mimeType);
595
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
596

    
597
        InputStream data = CNodeService.getInstance(request).get(session, id);
598

    
599
        OutputStream out = response.getOutputStream();
600
        response.setStatus(200);
601
        IOUtils.copyLarge(data, out);
602

    
603
    }
604

    
605
    /**
606
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
607
     * 
608
     * @param guid
609
     *            ID of data object to be read
610
     * @throws NotImplemented
611
     * @throws InvalidRequest
612
     * @throws NotFound
613
     * @throws NotAuthorized
614
     * @throws ServiceFailure
615
     * @throws InvalidToken
616
     * @throws IOException
617
     * @throws JiBXException
618
     */
619
    protected void getSystemMetadataObject(String guid) throws InvalidToken,
620
            ServiceFailure, NotAuthorized, NotFound, InvalidRequest,
621
            NotImplemented, IOException, JiBXException {
622

    
623
        Identifier id = new Identifier();
624
        id.setValue(guid);
625
        SystemMetadata sysmeta = CNodeService.getInstance(request)
626
                .getSystemMetadata(session, id);
627

    
628
        response.setContentType("text/xml");
629
        response.setStatus(200);
630
        OutputStream out = response.getOutputStream();
631

    
632
        // Serialize and write it to the output stream
633
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
634
    }
635

    
636
    /**
637
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler >
638
     * handleInsertOrUpdateAction
639
     * 
640
     * @param guid
641
     *            - ID of data object to be inserted or updated. If action is
642
     *            update, the pid is the existing pid. If insert, the pid is the
643
     *            new one
644
     * @throws InvalidRequest
645
     * @throws ServiceFailure
646
     * @throws IdentifierNotUnique
647
     * @throws JiBXException
648
     * @throws NotImplemented
649
     * @throws InvalidSystemMetadata
650
     * @throws InsufficientResources
651
     * @throws UnsupportedType
652
     * @throws NotAuthorized
653
     * @throws InvalidToken
654
     * @throws IOException
655
     * @throws IllegalAccessException
656
     * @throws InstantiationException
657
     */
658
    protected void putObject(String action) throws ServiceFailure,
659
            InvalidRequest, IdentifierNotUnique, JiBXException, InvalidToken,
660
            NotAuthorized, UnsupportedType, InsufficientResources,
661
            InvalidSystemMetadata, NotImplemented, IOException,
662
            InstantiationException, IllegalAccessException {
663
    	
664
        // Read the incoming data from its Mime Multipart encoding
665
        Map<String, File> files = collectMultipartFiles();
666
        
667
	    // get the encoded pid string from the body and make the object
668
        String pidString = multipartparams.get("pid").get(0);
669
        Identifier pid = new Identifier();
670
        pid.setValue(pidString);
671
        
672
        logMetacat.debug("putObject: " + pid.getValue() + "/" + action);
673
        
674
        InputStream object = null;
675
        InputStream sysmeta = null;
676

    
677
        File smFile = files.get("sysmeta");
678
        sysmeta = new FileInputStream(smFile);
679
        File objFile = files.get("object");
680
        object = new FileInputStream(objFile);
681

    
682
        if (action.equals(FUNCTION_NAME_INSERT)) { // handle inserts
683

    
684
            logMetacat.debug("Commence creation...");
685
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(
686
                    SystemMetadata.class, sysmeta);
687

    
688
           
689
            logMetacat.debug("creating object with pid " + pid.getValue());
690
            Identifier rId = CNodeService.getInstance(request).create(session, pid, object, smd);
691

    
692
            OutputStream out = response.getOutputStream();
693
            response.setStatus(200);
694
            response.setContentType("text/xml");
695

    
696
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
697

    
698
        } else {
699
            throw new InvalidRequest("1000", "Operation must be create.");
700
        }
701
    }
702

    
703
    /**
704
     * List the object formats registered with the system
705
     * 
706
     * @throws NotImplemented
707
     * @throws InsufficientResources
708
     * @throws NotFound
709
     * @throws ServiceFailure
710
     * @throws InvalidRequest
711
     * @throws IOException
712
     * @throws JiBXException
713
     */
714
    private void listFormats() throws InvalidRequest, ServiceFailure, NotFound,
715
            InsufficientResources, NotImplemented, IOException, JiBXException {
716
        logMetacat.debug("Entering listFormats()");
717

    
718
        ObjectFormatList objectFormatList = CNodeService.getInstance(request)
719
                .listFormats();
720
        // get the response output stream
721
        OutputStream out = response.getOutputStream();
722
        response.setStatus(200);
723
        response.setContentType("text/xml");
724

    
725
        // style the object with a processing directive
726
        String stylesheet = null;
727
        try {
728
            stylesheet = PropertyService.getProperty("dataone.types.xsl");
729
        } catch (PropertyNotFoundException e) {
730
            logMetacat.warn("Could not locate DataONE types XSLT: "
731
                    + e.getMessage());
732
        }
733

    
734
        TypeMarshaller.marshalTypeToOutputStream(objectFormatList, out,
735
                stylesheet);
736

    
737
    }
738
    
739
    private void listChecksumAlgorithms() throws IOException, ServiceFailure,
740
			NotImplemented, JiBXException {
741
		logMetacat.debug("Entering listFormats()");
742

    
743
		ChecksumAlgorithmList result = CNodeService.getInstance(request).listChecksumAlgorithms();
744

    
745
		// get the response output stream
746
		OutputStream out = response.getOutputStream();
747
		response.setStatus(200);
748
		response.setContentType("text/xml");
749

    
750
		TypeMarshaller.marshalTypeToOutputStream(result, out);
751

    
752
	}
753
    
754
    /**
755
     * http://mule1.dataone.org/ArchitectureDocs-current/apis/CN_APIs.html#CNRead.describe
756
     * @param pid
757
     * @throws InvalidToken
758
     * @throws ServiceFailure
759
     * @throws NotAuthorized
760
     * @throws NotFound
761
     * @throws NotImplemented
762
     * @throws InvalidRequest
763
     */
764
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
765
    {
766
        response.setContentType("text/xml");
767
        
768
        Identifier id = new Identifier();
769
        id.setValue(pid);
770
        
771
        DescribeResponse dr = null;
772
        try {
773
            dr = CNodeService.getInstance(request).describe(session, id);
774
        } catch (BaseException e) {
775
        	response.setStatus(e.getCode());
776
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
777
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
778
            response.addHeader("DataONE-Exception-Description", e.getDescription());
779
            response.addHeader("DataONE-Exception-PID", id.getValue());
780
            return;
781
		}
782
        
783
        response.setStatus(200);
784
        //response.addHeader("pid", pid);
785
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
786
        response.addHeader("Content-Length", dr.getContent_Length() + "");
787
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
788
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
789
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
790

    
791
    }
792
    
793
    /**
794
     * Handle delete 
795
     * @param pid ID of data object to be deleted
796
     * @throws IOException
797
     * @throws InvalidRequest 
798
     * @throws NotImplemented 
799
     * @throws NotFound 
800
     * @throws NotAuthorized 
801
     * @throws ServiceFailure 
802
     * @throws InvalidToken 
803
     * @throws JiBXException 
804
     */
805
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException 
806
    {
807

    
808
        OutputStream out = response.getOutputStream();
809
        response.setStatus(200);
810
        response.setContentType("text/xml");
811

    
812
        Identifier id = new Identifier();
813
        id.setValue(pid);
814

    
815
        logMetacat.debug("Calling delete for identifier " + pid);
816
        CNodeService.getInstance(request).delete(session, id);
817
        TypeMarshaller.marshalTypeToOutputStream(id, out);
818
        
819
    }
820
    
821
    /**
822
     * Archives the given pid
823
     * @param pid
824
     * @throws InvalidToken
825
     * @throws ServiceFailure
826
     * @throws NotAuthorized
827
     * @throws NotFound
828
     * @throws NotImplemented
829
     * @throws IOException
830
     * @throws JiBXException
831
     */
832
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
833

    
834
        OutputStream out = response.getOutputStream();
835
        response.setStatus(200);
836
        response.setContentType("text/xml");
837

    
838
        Identifier id = new Identifier();
839
        id.setValue(pid);
840

    
841
        logMetacat.debug("Calling archive");
842
        CNodeService.getInstance(request).archive(session, id);
843
        
844
        TypeMarshaller.marshalTypeToOutputStream(id, out);
845
        
846
    }
847

    
848
    /**
849
     * Return the requested object format
850
     * 
851
     * @param fmtidStr
852
     *            the requested format identifier as a string
853
     * @throws NotImplemented
854
     * @throws InsufficientResources
855
     * @throws NotFound
856
     * @throws ServiceFailure
857
     * @throws InvalidRequest
858
     * @throws IOException
859
     * @throws JiBXException
860
     */
861
    private void getFormat(String fmtidStr) throws InvalidRequest,
862
            ServiceFailure, NotFound, InsufficientResources, NotImplemented,
863
            IOException, JiBXException {
864
        logMetacat.debug("Entering listFormats()");
865

    
866
        ObjectFormatIdentifier fmtid = new ObjectFormatIdentifier();
867
        fmtid.setValue(fmtidStr);
868

    
869
        // get the specified object format
870
        ObjectFormat objectFormat = CNodeService.getInstance(request)
871
                .getFormat(fmtid);
872

    
873
        OutputStream out = response.getOutputStream();
874
        response.setStatus(200);
875
        response.setContentType("text/xml");
876

    
877
        TypeMarshaller.marshalTypeToOutputStream(objectFormat, out);
878

    
879
    }
880

    
881
    /**
882
     * Reserve the given Identifier
883
     * 
884
     * @throws InvalidToken
885
     * @throws ServiceFailure
886
     * @throws NotAuthorized
887
     * @throws IdentifierNotUnique
888
     * @throws NotImplemented
889
     * @throws InvalidRequest
890
     */
891
    private void reserve() 
892
        throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
893
        NotImplemented, InvalidRequest {
894
        Identifier pid = null;
895
        String scope = null;
896
        String format = null;
897

    
898
        // Parse the params out of the multipart form data
899
        logMetacat.debug("Parsing reserve parameters from the mime multipart entity");
900
        try {
901
            collectMultipartParams();
902
            
903
        } catch (FileUploadException e1) {
904
            String msg = "FileUploadException: Couldn't parse the mime multipart information: " +
905
            e1.getMessage();
906
            logMetacat.debug(msg);
907
            throw new ServiceFailure("4210", msg);
908

    
909
        } catch (IOException e1) {
910
            String msg = "IOException: Couldn't parse the mime multipart information: " +
911
            e1.getMessage();
912
            logMetacat.debug(msg);
913
            throw new ServiceFailure("4210", msg);
914
        
915
        } catch (Exception e1) {
916
            String msg = "Exception: Couldn't parse the mime multipart information: " +
917
            e1.getMessage();
918
            logMetacat.debug(msg);
919
            throw new ServiceFailure("4210", msg);
920

    
921
        }
922
        
923
        // gather the params
924
        try {
925
            String id = multipartparams.get("pid").get(0);
926
            pid = new Identifier();
927
            pid.setValue(id);
928
            
929
        } catch (NullPointerException e) {
930
            String msg = "The 'pid' must be provided as a parameter and was not.";
931
            logMetacat.error(msg);
932
            throw new InvalidRequest("4200", msg);
933
 
934
        }
935
        
936
        // call the implementation
937
        try {
938
            Identifier resultPid = CNodeService.getInstance(request).reserveIdentifier(session, pid);
939
            OutputStream out = response.getOutputStream();
940
            response.setStatus(200);
941
            response.setContentType("text/xml");
942
            // send back the reserved pid
943
            TypeMarshaller.marshalTypeToOutputStream(resultPid, out);
944
            
945
        } catch (IOException e) {
946
            String msg = "Couldn't write the identifier to the response output stream: " +
947
                e.getMessage();
948
            logMetacat.debug(msg);
949
            throw new ServiceFailure("4210", msg);
950
        
951
        } catch (JiBXException e) {
952
            String msg = "Couldn't marshall the identifier to the response output stream: " +
953
            e.getMessage();
954
            logMetacat.debug(msg);
955
            throw new ServiceFailure("4210", msg);
956
            
957
        }
958
    }
959

    
960
    /**
961
     * 
962
     * @param id
963
     * @throws InvalidRequest
964
     * @throws InvalidToken
965
     * @throws ServiceFailure
966
     * @throws NotAuthorized
967
     * @throws NotFound
968
     * @throws NotImplemented
969
     * @throws IOException
970
     * @throws JiBXException
971
     */
972
    private void resolve(String id) throws InvalidRequest, InvalidToken,
973
            ServiceFailure, NotAuthorized, NotFound, NotImplemented,
974
            IOException, JiBXException {
975
        Identifier pid = new Identifier();
976
        pid.setValue(id);
977
        ObjectLocationList locationList = CNodeService.getInstance(request)
978
                .resolve(session, pid);
979
        OutputStream out = response.getOutputStream();
980
        response.setStatus(200);
981
        response.setContentType("text/xml");
982
        TypeMarshaller.marshalTypeToOutputStream(locationList, out);
983

    
984
    }
985

    
986
    /**
987
     * Set the owner of a resource
988
     * 
989
     * @param id
990
     * @throws InvalidToken
991
     * @throws ServiceFailure
992
     * @throws NotFound
993
     * @throws NotAuthorized
994
     * @throws NotImplemented
995
     * @throws InvalidRequest
996
     * @throws IllegalAccessException
997
     * @throws InstantiationException
998
     * @throws VersionMismatch 
999
     */
1000
    private void owner(String id) 
1001
        throws InvalidToken, ServiceFailure, NotFound, NotAuthorized, 
1002
        NotImplemented, InvalidRequest, InstantiationException, 
1003
        IllegalAccessException, VersionMismatch {
1004

    
1005
        Identifier pid = new Identifier();
1006
        pid.setValue(id);
1007

    
1008
        long serialVersion = 0L;
1009
        String serialVersionStr = null;
1010
        String userIdStr = null;
1011
        Subject userId = null;
1012
        
1013
        // Parse the params out of the multipart form data
1014
        // Read the incoming data from its Mime Multipart encoding
1015
        logMetacat.debug("Parsing rights holder parameters from the mime multipart entity");
1016
        try {
1017
            collectMultipartParams();
1018
            
1019
        } catch (FileUploadException e1) {
1020
            String msg = "FileUploadException: Couldn't parse the mime multipart information: " +
1021
            e1.getMessage();
1022
            logMetacat.debug(msg);
1023
            throw new ServiceFailure("4490", msg);
1024

    
1025
        } catch (IOException e1) {
1026
            String msg = "IOException: Couldn't parse the mime multipart information: " +
1027
            e1.getMessage();
1028
            logMetacat.debug(msg);
1029
            throw new ServiceFailure("4490", msg);
1030
        
1031
        } catch (Exception e1) {
1032
            String msg = "Exception: Couldn't parse the mime multipart information: " +
1033
            e1.getMessage();
1034
            logMetacat.debug(msg);
1035
            throw new ServiceFailure("4490", msg);
1036

    
1037
        }
1038
        
1039
        // get the serialVersion
1040
        try {
1041
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1042
            serialVersion = new Long(serialVersionStr).longValue();
1043
            
1044
        } catch (NumberFormatException nfe) {
1045
            String msg = "The 'serialVersion' must be provided as a positive integer and was not.";
1046
            logMetacat.error(msg);
1047
            throw new InvalidRequest("4442", msg);
1048
                        
1049
        } catch (NullPointerException e) {
1050
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1051
            logMetacat.error(msg);
1052
            throw new InvalidRequest("4442", msg);
1053
            
1054
        }
1055

    
1056
        // get the subject userId that will become the rights holder
1057
        try {
1058
            userIdStr = multipartparams.get("userId").get(0);
1059
            userId = new Subject();
1060
            userId.setValue(userIdStr);
1061
                                    
1062
        } catch (NullPointerException e) {
1063
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1064
            logMetacat.error(msg);
1065
            throw new InvalidRequest("4442", msg);
1066
            
1067
        }
1068

    
1069
        // set the rights holder
1070
        Identifier retPid = CNodeService.getInstance(request).setRightsHolder(session, pid, userId, serialVersion);
1071
        
1072
        try {
1073
            OutputStream out = response.getOutputStream();
1074
            response.setStatus(200);
1075
            response.setContentType("text/xml");
1076
            TypeMarshaller.marshalTypeToOutputStream(retPid, out);
1077
            
1078
        } catch (IOException e) {
1079
            String msg = "Couldn't write the identifier to the response output stream: " +
1080
                e.getMessage();
1081
            logMetacat.debug(msg);
1082
            throw new ServiceFailure("4490", msg);
1083
        
1084
        } catch (JiBXException e) {
1085
            String msg = "Couldn't marshall the identifier to the response output stream: " +
1086
            e.getMessage();
1087
            logMetacat.debug(msg);
1088
            throw new ServiceFailure("4490", msg);
1089
            
1090
        }
1091
    }
1092

    
1093
    /**
1094
     * Processes the authorization check for given id
1095
     * 
1096
     * @param id
1097
     * @return
1098
     * @throws ServiceFailure
1099
     * @throws InvalidToken
1100
     * @throws NotFound
1101
     * @throws NotAuthorized
1102
     * @throws NotImplemented
1103
     * @throws InvalidRequest
1104
     */
1105
    private boolean isAuthorized(String id) throws ServiceFailure,
1106
            InvalidToken, NotFound, NotAuthorized, NotImplemented,
1107
            InvalidRequest {
1108
        Identifier pid = new Identifier();
1109
        pid.setValue(id);
1110
        String permission = params.get("action")[0];
1111
        boolean result = CNodeService.getInstance(request).isAuthorized(
1112
                session, pid, Permission.convert(permission));
1113
        response.setStatus(200);
1114
        response.setContentType("text/xml");
1115
        return result;
1116
    }
1117

    
1118
    /**
1119
     * Register System Metadata without data or metadata object
1120
     * 
1121
     * @param pid
1122
     *            identifier for System Metadata entry
1123
     * @throws JiBXException
1124
     * @throws FileUploadException
1125
     * @throws IOException
1126
     * @throws InvalidRequest
1127
     * @throws ServiceFailure
1128
     * @throws InvalidSystemMetadata
1129
     * @throws NotAuthorized
1130
     * @throws NotImplemented
1131
     * @throws IllegalAccessException
1132
     * @throws InstantiationException
1133
     */
1134
    protected void registerSystemMetadata()
1135
            throws ServiceFailure, InvalidRequest, IOException,
1136
            FileUploadException, JiBXException, NotImplemented, NotAuthorized,
1137
            InvalidSystemMetadata, InstantiationException,
1138
            IllegalAccessException {
1139
    	
1140
    	// Read the incoming data from its Mime Multipart encoding
1141
        Map<String, File> files = collectMultipartFiles();
1142
        
1143
    	// get the encoded pid string from the body and make the object
1144
        String pidString = multipartparams.get("pid").get(0);
1145
        Identifier pid = new Identifier();
1146
        pid.setValue(pidString);
1147
        
1148
        logMetacat.debug("registerSystemMetadata: " + pid);
1149

    
1150
        // get the system metadata from the request
1151
        File smFile = files.get("sysmeta");
1152
        FileInputStream sysmeta = new FileInputStream(smFile);
1153
        SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1154

    
1155
        logMetacat.debug("registering system metadata with pid " + pid.getValue());
1156
        Identifier retGuid = CNodeService.getInstance(request).registerSystemMetadata(session, pid, systemMetadata);
1157

    
1158
        OutputStream out = response.getOutputStream();
1159
        response.setStatus(200);
1160
        response.setContentType("text/xml");
1161
        
1162
        TypeMarshaller.marshalTypeToOutputStream(retGuid, out);
1163

    
1164
    }
1165

    
1166
    /**
1167
     * set the access perms on a document
1168
     * 
1169
     * @throws JiBXException
1170
     * @throws InvalidRequest
1171
     * @throws NotImplemented
1172
     * @throws NotAuthorized
1173
     * @throws NotFound
1174
     * @throws ServiceFailure
1175
     * @throws InvalidToken
1176
     * @throws IllegalAccessException
1177
     * @throws InstantiationException
1178
     * @throws IOException
1179
     * @throws SAXException
1180
     * @throws ParserConfigurationException
1181
     * @throws VersionMismatch 
1182
     */
1183
    protected void setAccess(String pid) throws JiBXException, InvalidToken,
1184
            ServiceFailure, NotFound, NotAuthorized, NotImplemented,
1185
            InvalidRequest, IOException, InstantiationException,
1186
            IllegalAccessException, ParserConfigurationException, SAXException, VersionMismatch {
1187

    
1188
        long serialVersion = 0L;
1189
        String serialVersionStr = null;
1190
        
1191
        // parse the accessPolicy
1192
        Map<String, File> files = collectMultipartFiles();        
1193
        AccessPolicy accessPolicy = TypeMarshaller.unmarshalTypeFromFile(AccessPolicy.class, files.get("accessPolicy"));;
1194

    
1195
        // get the serialVersion
1196
        try {
1197
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1198
            serialVersion = new Long(serialVersionStr).longValue();
1199

    
1200
        } catch (NumberFormatException nfe) {
1201
            String msg = "The 'serialVersion' must be provided as a positive integer and was not.";
1202
            logMetacat.error(msg);
1203
            throw new InvalidRequest("4402", msg);
1204
            
1205
        } catch (NullPointerException e) {
1206
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1207
            logMetacat.error(msg);
1208
            throw new InvalidRequest("4402", msg);
1209

    
1210
        }
1211

    
1212
        Identifier id = new Identifier();
1213
        id.setValue(pid);
1214

    
1215
        CNodeService.getInstance(request).setAccessPolicy(session, id,
1216
                accessPolicy, serialVersion);
1217

    
1218
    }
1219

    
1220
    /**
1221
     * List the objects
1222
     * 
1223
     * @throws NotImplemented
1224
     * @throws InvalidRequest
1225
     * @throws NotAuthorized
1226
     * @throws ServiceFailure
1227
     * @throws InvalidToken
1228
     * @throws NotFound
1229
     * @throws IOException
1230
     * @throws JiBXException
1231
     * @throws Exception
1232
     */
1233
    private void listObjects() throws InvalidToken, ServiceFailure,
1234
            NotAuthorized, InvalidRequest, NotImplemented, NotFound,
1235
            IOException, JiBXException {
1236

    
1237
        Date startTime = null;
1238
        Date endTime = null;
1239
        ObjectFormatIdentifier formatId = null;
1240
        Identifier identifier = null;
1241
        boolean replicaStatus = true;
1242
        int start = 0;
1243
        int count = 1000;
1244
        Enumeration<String> paramlist = request.getParameterNames();
1245
        while (paramlist.hasMoreElements()) {
1246
            // parse the params and make the call
1247
            String name = paramlist.nextElement();
1248
            String[] values = request.getParameterValues(name);
1249
            String value = null;
1250
            if (values != null && values.length > 0) {
1251
            	value = values[0];
1252
            	value = EncodingUtilities.decodeString(value);
1253
            }
1254

    
1255
            if (name.equals("fromDate") && value != null) {
1256
                try {
1257
                    startTime = DateTimeMarshaller.deserializeDateToUTC(value);
1258
                } catch (Exception e) {
1259
                    // if we can't parse it, just don't use the startTime param
1260
                    logMetacat.warn("Could not parse fromDate: " + value);
1261
                    startTime = null;
1262
                }
1263
            } else if (name.equals("toDate") && value != null) {
1264
                try {
1265
                    endTime = DateTimeMarshaller.deserializeDateToUTC(value);
1266
                } catch (Exception e) {
1267
                    // if we can't parse it, just don't use the endTime param
1268
                    logMetacat.warn("Could not parse toDate: " + value);
1269
                    endTime = null;
1270
                }
1271
            } else if (name.equals("formatId") && value != null) {
1272
            	formatId = new ObjectFormatIdentifier();
1273
            	formatId.setValue(value);
1274
            } else if (name.equals("identifier") && value != null) {
1275
            	identifier = new Identifier();
1276
            	identifier.setValue(value);
1277
            } else if (name.equals("replicaStatus") && value != null) {
1278
                replicaStatus = Boolean.parseBoolean(value);
1279
            } else if (name.equals("start") && value != null) {
1280
                start = Integer.valueOf(value);
1281
            } else if (name.equals("count") && value != null) {
1282
                count = Integer.valueOf(value);
1283
            }
1284
        }
1285
        // make the call
1286
        logMetacat.debug("session: " + session + " fromDate: " + startTime
1287
                + " toDate: " + endTime + " formatId: " + formatId
1288
                + " replicaStatus: " + replicaStatus + " start: " + start
1289
                + " count: " + count);        
1290

    
1291
        // get the list
1292
        ObjectList ol = CNodeService.getInstance(request).listObjects(session,
1293
                startTime, endTime, formatId, identifier, replicaStatus, start, count);
1294

    
1295
        // send it
1296
        OutputStream out = response.getOutputStream();
1297
        response.setStatus(200);
1298
        response.setContentType("text/xml");
1299

    
1300
        // style the object with a processing directive
1301
        String stylesheet = null;
1302
        try {
1303
            stylesheet = PropertyService.getProperty("dataone.types.xsl");
1304
        } catch (PropertyNotFoundException e) {
1305
            logMetacat.warn("Could not locate DataONE types XSLT: "
1306
                    + e.getMessage());
1307
        }
1308

    
1309
        // Serialize and write it to the output stream
1310
        TypeMarshaller.marshalTypeToOutputStream(ol, out, stylesheet);
1311
    }
1312

    
1313
    /**
1314
     * Pass the request to get node replication authorization to CNodeService
1315
     * 
1316
     * @param pid
1317
     *            the identifier of the object to get authorization to replicate
1318
     * 
1319
     * @throws NotImplemented
1320
     * @throws NotAuthorized
1321
     * @throws InvalidToken
1322
     * @throws ServiceFailure
1323
     * @throws NotFound
1324
     * @throws InvalidRequest
1325
     */
1326
    public boolean isNodeAuthorized(String pid) throws NotImplemented,
1327
            NotAuthorized, InvalidToken, ServiceFailure, NotFound,
1328
            InvalidRequest {
1329

    
1330
        boolean result = false;
1331
        Subject targetNodeSubject = new Subject();
1332
        String nodeSubject = null;
1333
        String replPermission = null;
1334

    
1335
        // get the pid
1336
        Identifier identifier = new Identifier();
1337
        identifier.setValue(pid);
1338

    
1339
        // get the target node subject
1340
        try {
1341
            nodeSubject = params.get("targetNodeSubject")[0];
1342
            targetNodeSubject.setValue(nodeSubject);
1343

    
1344
        } catch (NullPointerException e) {
1345
            String msg = "The 'targetNodeSubject' must be provided as a parameter and was not.";
1346
            logMetacat.error(msg);
1347
            throw new InvalidRequest("4873", msg);
1348

    
1349
        }
1350

    
1351
        result = CNodeService.getInstance(request).isNodeAuthorized(session, targetNodeSubject, identifier);
1352

    
1353
        response.setStatus(200);
1354
        response.setContentType("text/xml");
1355
        return result;
1356

    
1357
    }
1358

    
1359
    /**
1360
     * Pass the request to set the replication policy to CNodeService
1361
     * 
1362
     * @param pid
1363
     *            the identifier of the object to set the replication policy on
1364
     * 
1365
     * @throws NotImplemented
1366
     * @throws NotFound
1367
     * @throws NotAuthorized
1368
     * @throws ServiceFailure
1369
     * @throws InvalidRequest
1370
     * @throws InvalidToken
1371
     * @throws IOException
1372
     * @throws InstantiationException
1373
     * @throws IllegalAccessException
1374
     * @throws JiBXException
1375
     * @throws VersionMismatch 
1376
     */
1377
    public boolean setReplicationPolicy(String pid) throws NotImplemented,
1378
            NotFound, NotAuthorized, ServiceFailure, InvalidRequest,
1379
            InvalidToken, IOException, InstantiationException,
1380
            IllegalAccessException, JiBXException, VersionMismatch {
1381

    
1382
        boolean result = false;
1383
        long serialVersion = 0L;
1384
        String serialVersionStr = null;
1385

    
1386
        Identifier identifier = new Identifier();
1387
        identifier.setValue(pid);
1388

    
1389
        // parse the policy
1390
        Map<String, File> files = collectMultipartFiles();        
1391
        ReplicationPolicy policy = TypeMarshaller.unmarshalTypeFromFile(ReplicationPolicy.class, files.get("policy"));
1392

    
1393
        // get the serialVersion
1394
        try {
1395
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1396
            serialVersion = new Long(serialVersionStr).longValue();
1397

    
1398
        } catch (NullPointerException e) {
1399
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1400
            logMetacat.error(msg);
1401
            throw new InvalidRequest("4883", msg);
1402

    
1403
        }
1404
        result = CNodeService.getInstance(request).setReplicationPolicy(
1405
                session, identifier, policy, serialVersion);
1406
        response.setStatus(200);
1407
        response.setContentType("text/xml");
1408
        return result;
1409

    
1410
    }
1411
    
1412
    /**
1413
     * Update the system metadata for a given pid, setting it to be obsoleted
1414
     * by the obsoletedByPid
1415
     *  
1416
     * @param pid
1417
     * @return
1418
     * @throws NotImplemented
1419
     * @throws NotFound
1420
     * @throws NotAuthorized
1421
     * @throws ServiceFailure
1422
     * @throws InvalidRequest
1423
     * @throws InvalidToken
1424
     * @throws InstantiationException
1425
     * @throws IllegalAccessException
1426
     * @throws VersionMismatch
1427
     */
1428
    public boolean setObsoletedBy(String pid) 
1429
        throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
1430
        InvalidRequest, InvalidToken, InstantiationException, 
1431
        IllegalAccessException, VersionMismatch {
1432

    
1433
        boolean result = false;
1434
        long serialVersion = 0L;
1435
        String serialVersionStr = null;
1436

    
1437
        Identifier identifier = new Identifier();
1438
        identifier.setValue(pid);
1439
        String obsoletedByPidString = null;
1440
        Identifier obsoletedByPid = null;
1441

    
1442
        // Parse the params out of the multipart form data
1443
        // Read the incoming data from its Mime Multipart encoding
1444
        logMetacat.debug("Parsing rights holder parameters from the mime multipart entity");
1445
        try {
1446
            collectMultipartParams();
1447
            
1448
        } catch (FileUploadException e1) {
1449
            String msg = "FileUploadException: Couldn't parse the mime multipart information: " +
1450
            e1.getMessage();
1451
            logMetacat.debug(msg);
1452
            throw new ServiceFailure("4941", msg);
1453

    
1454
        } catch (IOException e1) {
1455
            String msg = "IOException: Couldn't parse the mime multipart information: " +
1456
            e1.getMessage();
1457
            logMetacat.debug(msg);
1458
            throw new ServiceFailure("4941", msg);
1459
        
1460
        } catch (Exception e1) {
1461
            String msg = "Exception: Couldn't parse the mime multipart information: " +
1462
            e1.getMessage();
1463
            logMetacat.debug(msg);
1464
            throw new ServiceFailure("4941", msg);
1465

    
1466
        }
1467

    
1468
        // get the obsoletedByPid
1469
        try {
1470
            obsoletedByPidString = multipartparams.get("obsoletedByPid").get(0);
1471
            obsoletedByPid = new Identifier();
1472
            obsoletedByPid.setValue(obsoletedByPidString);
1473
        } catch (NullPointerException e) {
1474
            String msg = "The 'obsoletedByPid' must be provided as a parameter and was not.";
1475
            logMetacat.error(msg);
1476
            throw new InvalidRequest("4883", msg);
1477
        }
1478

    
1479
        // get the serialVersion
1480
        try {
1481
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1482
            serialVersion = new Long(serialVersionStr).longValue();
1483
            
1484
        } catch (NumberFormatException nfe) {
1485
            String msg = "The 'serialVersion' must be provided as a positive integer and was not.";
1486
            logMetacat.error(msg);
1487
            throw new InvalidRequest("4942", msg);
1488
                        
1489
        } catch (NullPointerException e) {
1490
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1491
            logMetacat.error(msg);
1492
            throw new InvalidRequest("4942", msg);
1493
            
1494
        }
1495
        result = CNodeService.getInstance(request).setObsoletedBy(session,
1496
            identifier, obsoletedByPid, serialVersion);
1497
        response.setStatus(200);
1498
        response.setContentType("text/xml");
1499
        return result;
1500

    
1501
    }
1502
    
1503
    /**
1504
     * Delete the replica entry with the given nodeId for the given pid
1505
     * 
1506
     * @param pid
1507
     * @return
1508
     * @throws NotImplemented
1509
     * @throws NotFound
1510
     * @throws NotAuthorized
1511
     * @throws ServiceFailure
1512
     * @throws InvalidRequest
1513
     * @throws InvalidToken
1514
     * @throws InstantiationException
1515
     * @throws IllegalAccessException
1516
     * @throws VersionMismatch
1517
     */
1518
    public boolean deleteReplica(String pid) 
1519
        throws NotImplemented, NotFound, NotAuthorized, ServiceFailure, 
1520
        InvalidRequest, InvalidToken, InstantiationException, 
1521
        IllegalAccessException, VersionMismatch {
1522

    
1523
        boolean result = false;
1524
        long serialVersion = 0L;
1525
        String serialVersionStr = null;
1526

    
1527
        Identifier identifier = new Identifier();
1528
        identifier.setValue(pid);
1529

    
1530
        NodeReference nodeId = null;
1531
        
1532
        // Parse the params out of the multipart form data
1533
        // Read the incoming data from its Mime Multipart encoding
1534
        logMetacat.debug("Parsing delete replica parameters from the mime multipart entity");
1535
        try {
1536
            collectMultipartParams();
1537
            
1538
        } catch (FileUploadException e1) {
1539
            String msg = "FileUploadException: Couldn't parse the mime multipart information: " +
1540
            e1.getMessage();
1541
            logMetacat.debug(msg);
1542
            throw new ServiceFailure("4951", msg);
1543

    
1544
        } catch (IOException e1) {
1545
            String msg = "IOException: Couldn't parse the mime multipart information: " +
1546
            e1.getMessage();
1547
            logMetacat.debug(msg);
1548
            throw new ServiceFailure("4951", msg);
1549
        
1550
        } catch (Exception e1) {
1551
            String msg = "Exception: Couldn't parse the mime multipart information: " +
1552
            e1.getMessage();
1553
            logMetacat.debug(msg);
1554
            throw new ServiceFailure("4951", msg);
1555

    
1556
        }
1557
        
1558
        // get the nodeId param
1559
        try {
1560
            String nodeIdString = multipartparams.get("nodeId").get(0);
1561
            nodeId = new NodeReference();
1562
            nodeId.setValue(nodeIdString);
1563
            
1564
        } catch (NullPointerException e) {
1565
            String msg = "The 'nodeId' must be provided as a parameter and was not.";
1566
            logMetacat.error(msg);
1567
            throw new InvalidRequest("4952", msg);
1568
        }
1569

    
1570
        // get the serialVersion
1571
        try {
1572
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1573
            serialVersion = new Long(serialVersionStr).longValue();
1574
            
1575
        } catch (NumberFormatException nfe) {
1576
            String msg = "The 'serialVersion' must be provided as a positive integer and was not.";
1577
            logMetacat.error(msg);
1578
            throw new InvalidRequest("4952", msg);
1579
                        
1580
        } catch (NullPointerException e) {
1581
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1582
            logMetacat.error(msg);
1583
            throw new InvalidRequest("4952", msg);
1584
            
1585
        }
1586
        result = CNodeService.getInstance(request).deleteReplicationMetadata(
1587
                session, identifier, nodeId, serialVersion);
1588
        response.setStatus(200);
1589
        response.setContentType("text/xml");
1590
        return result;
1591

    
1592
    }
1593

    
1594
    /**
1595
     * Pass the request to set the replication status to CNodeService
1596
     * 
1597
     * @param pid
1598
     *            the identifier of the object to set the replication status on
1599
     * 
1600
     * @throws ServiceFailure
1601
     * @throws NotImplemented
1602
     * @throws InvalidToken
1603
     * @throws NotAuthorized
1604
     * @throws InvalidRequest
1605
     * @throws NotFound
1606
     * @throws JiBXException 
1607
     * @throws IllegalAccessException 
1608
     * @throws InstantiationException 
1609
     * @throws IOException 
1610
     */
1611
    public boolean setReplicationStatus(String pid) throws ServiceFailure,
1612
            NotImplemented, InvalidToken, NotAuthorized, InvalidRequest,
1613
            NotFound {
1614
        
1615
        boolean result = false;
1616
        Identifier identifier = new Identifier();
1617
        identifier.setValue(pid);
1618
        BaseException failure = null;
1619
        ReplicationStatus status = null;
1620
        String replicationStatus = null;
1621
        NodeReference targetNodeRef = null;
1622
        String targetNode = null;
1623

    
1624
        // Parse the params out of the multipart form data
1625
        // Read the incoming data from its Mime Multipart encoding
1626
        logMetacat.debug("Parsing ReplicaStatus from the mime multipart entity");
1627

    
1628
        try {
1629
        	// parse the policy
1630
            Map<String, File> files = collectMultipartFiles();        
1631
            failure = ExceptionHandler.deserializeXml(new FileInputStream(files.get("failure")), 
1632
                    "Replication failed for an unknown reason.");
1633
            
1634
        } catch (Exception e2) {
1635
            throw new ServiceFailure("4700", "Couldn't resolve the multipart request: " +
1636
                e2.getMessage());
1637
            
1638
        }
1639
        
1640
        // get the replication status param
1641
        try {
1642
            replicationStatus = multipartparams.get("status").get(0);
1643
            status = ReplicationStatus.convert(replicationStatus);
1644

    
1645
        } catch (NullPointerException npe) {
1646

    
1647
            logMetacat.debug("The 'status' parameter was not found in the "
1648
                    + "multipartparams map.  Trying the params map.");
1649

    
1650
            try {
1651
                replicationStatus = params.get("status")[0];
1652
                status = ReplicationStatus.convert(replicationStatus
1653
                        .toLowerCase());
1654

    
1655
            } catch (Exception e) {
1656
                String msg = "The 'status' must be provided as a parameter and was not.";
1657
                logMetacat.error(msg);
1658
                throw new InvalidRequest("4730", msg);
1659

    
1660
            }
1661

    
1662
        }
1663

    
1664
        // get the target node reference param
1665
        try {
1666
            targetNode = multipartparams.get("nodeRef").get(0);
1667
            targetNodeRef = new NodeReference();
1668
            targetNodeRef.setValue(targetNode);
1669

    
1670
        } catch (NullPointerException e) {
1671
            logMetacat.debug("The 'nodeRef' parameter was not found in the "
1672
                    + "multipartparams map.  Trying the params map.");
1673

    
1674
            try {
1675
                targetNode = params.get("nodeRef")[0];
1676
                targetNodeRef = new NodeReference();
1677
                targetNodeRef.setValue(targetNode);
1678

    
1679
            } catch (Exception e1) {
1680
                String msg = "The 'nodeRef' must be provided as a parameter and was not.";
1681
                logMetacat.error(msg);
1682
                throw new InvalidRequest("4730", msg);
1683

    
1684
            }
1685

    
1686
        }
1687

    
1688
        result = CNodeService.getInstance(request).setReplicationStatus(
1689
                session, identifier, targetNodeRef, status, failure);
1690
        response.setStatus(200);
1691
        response.setContentType("text/xml");
1692
        return result;
1693

    
1694
    }
1695

    
1696
    /**
1697
     * Pass the request to update the replication metadata to CNodeService
1698
     * 
1699
     * @param pid
1700
     *            the identifier of the object to update the replication
1701
     *            metadata on
1702
     * 
1703
     * @throws ServiceFailure
1704
     * @throws NotImplemented
1705
     * @throws InvalidToken
1706
     * @throws NotAuthorized
1707
     * @throws InvalidRequest
1708
     * @throws NotFound
1709
     * @throws VersionMismatch 
1710
     * @throws JiBXException 
1711
     * @throws IOException 
1712
     * @throws IllegalAccessException 
1713
     * @throws InstantiationException 
1714
     */
1715
    public boolean updateReplicationMetadata(String pid) throws ServiceFailure,
1716
            NotImplemented, InvalidToken, NotAuthorized, InvalidRequest,
1717
            NotFound, VersionMismatch, InstantiationException, IllegalAccessException, IOException, JiBXException {
1718

    
1719
        boolean result = false;
1720
        long serialVersion = 0L;
1721
        String serialVersionStr = null;
1722
        Identifier identifier = new Identifier();
1723
        identifier.setValue(pid);
1724

    
1725
        // parse the replica
1726
        Map<String, File> files = collectMultipartFiles();        
1727
        Replica replica = TypeMarshaller.unmarshalTypeFromFile(Replica.class, files.get("replicaMetadata"));
1728

    
1729
        // get the serialVersion
1730
        try {
1731
            serialVersionStr = multipartparams.get("serialVersion").get(0);
1732
            serialVersion = new Long(serialVersionStr).longValue();
1733

    
1734
        } catch (NullPointerException e) {
1735
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
1736
            logMetacat.error(msg);
1737
            throw new InvalidRequest("4853", msg);
1738

    
1739
        }
1740

    
1741
        result = CNodeService.getInstance(request).updateReplicationMetadata(
1742
                session, identifier, replica, serialVersion);
1743
        response.setStatus(200);
1744
        response.setContentType("text/xml");
1745
        return result;
1746

    
1747
    }
1748
    
1749
    /**
1750
     * Update the system metadata for a specified pid
1751
     * @throws ServiceFailure
1752
     * @throws InvalidRequest
1753
     * @throws InstantiationException
1754
     * @throws IllegalAccessException
1755
     * @throws IOException
1756
     * @throws JiBXException
1757
     * @throws NotImplemented
1758
     * @throws NotAuthorized
1759
     * @throws InvalidSystemMetadata
1760
     * @throws InvalidToken
1761
     */
1762
    protected void updateSystemMetadata() throws ServiceFailure, InvalidRequest, InstantiationException, 
1763
                        IllegalAccessException, IOException, JiBXException, NotImplemented, NotAuthorized, InvalidSystemMetadata, InvalidToken {
1764
        // Read the incoming data from its Mime Multipart encoding
1765
        Map<String, File> files = collectMultipartFiles();
1766
        
1767
        // get the encoded pid string from the body and make the object
1768
        String pidString = multipartparams.get("pid").get(0);
1769
        Identifier pid = new Identifier();
1770
        pid.setValue(pidString);
1771
        
1772
        logMetacat.debug("updateSystemMetadata: " + pid);
1773

    
1774
        // get the system metadata from the request
1775
        File smFile = files.get("sysmeta");
1776
        FileInputStream sysmeta = new FileInputStream(smFile);
1777
        SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1778

    
1779
        logMetacat.debug("updating system metadata with pid " + pid.getValue());
1780
        
1781
        CNodeService.getInstance(request).updateSystemMetadata(session, pid, systemMetadata);
1782
    }
1783
    
1784
    private void doViews(String format, String pid) {
1785
        
1786
        OutputStream out = null;
1787
        CNodeService cnode = CNodeService.getInstance(request);
1788

    
1789
        try {
1790
            // get a list of views
1791
            if (pid != null) {
1792
                Identifier identifier = new Identifier();
1793
                identifier.setValue(pid);
1794
                InputStream stream = cnode.view(session, format, identifier);
1795

    
1796
                // set the content-type if we have it from the implementation
1797
                if (stream instanceof ContentTypeInputStream) {
1798
                    response.setContentType(((ContentTypeInputStream) stream).getContentType());
1799
                }
1800
                response.setStatus(200);
1801
                out = response.getOutputStream();
1802
                // write the results to the output stream
1803
                IOUtils.copyLarge(stream, out);
1804
                return;
1805
            } else {
1806
                // TODO: list the registered views
1807
                //BaseException ni = new NotImplemented("9999", "MN.listViews() is not implemented at this node");
1808
                //throw ni;
1809
                OptionList list = cnode.listViews(session);
1810
                
1811
                response.setContentType("text/xml");
1812
                response.setStatus(200);
1813
                TypeMarshaller.marshalTypeToOutputStream(list, response.getOutputStream());
1814
            }
1815
            
1816
            
1817
        } catch (BaseException be) {
1818
            // report Exceptions as clearly as possible
1819
            try {
1820
                out = response.getOutputStream();
1821
            } catch (IOException e) {
1822
                logMetacat.error("Could not get output stream from response", e);
1823
            }
1824
            serializeException(be, out);
1825
        } catch (Exception e) {
1826
            // report Exceptions as clearly and generically as possible
1827
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
1828
            try {
1829
                out = response.getOutputStream();
1830
            } catch (IOException ioe) {
1831
                logMetacat.error("Could not get output stream from response", ioe);
1832
            }
1833
            ServiceFailure se = new ServiceFailure("0000", e.getMessage());
1834
            serializeException(se, out);
1835
        }
1836
    }
1837

    
1838
}
(1-1/4)