Project

General

Profile

1 6264 leinfelder
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2011 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author$'
7
 *     '$Date$'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.restservice;
24
25
import java.io.ByteArrayInputStream;
26
import java.io.File;
27
import java.io.FileInputStream;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.text.DateFormat;
32
import java.text.ParseException;
33
import java.text.SimpleDateFormat;
34
import java.util.Date;
35
import java.util.Enumeration;
36
import java.util.Map;
37 6297 cjones
import java.util.TimeZone;
38 6264 leinfelder
39
import javax.servlet.ServletContext;
40
import javax.servlet.http.HttpServletRequest;
41
import javax.servlet.http.HttpServletResponse;
42 6472 leinfelder
import javax.xml.parsers.ParserConfigurationException;
43 6264 leinfelder
44 6273 leinfelder
import org.apache.commons.fileupload.FileUploadException;
45 6264 leinfelder
import org.apache.commons.io.IOUtils;
46
import org.apache.log4j.Logger;
47
import org.dataone.client.ObjectFormatCache;
48 6472 leinfelder
import org.dataone.mimemultipart.MultipartRequest;
49
import org.dataone.mimemultipart.MultipartRequestResolver;
50 6264 leinfelder
import org.dataone.service.exceptions.BaseException;
51
import org.dataone.service.exceptions.IdentifierNotUnique;
52
import org.dataone.service.exceptions.InsufficientResources;
53
import org.dataone.service.exceptions.InvalidRequest;
54
import org.dataone.service.exceptions.InvalidSystemMetadata;
55
import org.dataone.service.exceptions.InvalidToken;
56
import org.dataone.service.exceptions.NotAuthorized;
57
import org.dataone.service.exceptions.NotFound;
58
import org.dataone.service.exceptions.NotImplemented;
59
import org.dataone.service.exceptions.ServiceFailure;
60
import org.dataone.service.exceptions.SynchronizationFailed;
61
import org.dataone.service.exceptions.UnsupportedType;
62 6366 leinfelder
import org.dataone.service.types.v1.AccessPolicy;
63
import org.dataone.service.types.v1.Checksum;
64
import org.dataone.service.types.v1.DescribeResponse;
65
import org.dataone.service.types.v1.Event;
66
import org.dataone.service.types.v1.Identifier;
67
import org.dataone.service.types.v1.Log;
68
import org.dataone.service.types.v1.MonitorList;
69
import org.dataone.service.types.v1.Node;
70
import org.dataone.service.types.v1.NodeReference;
71
import org.dataone.service.types.v1.ObjectFormat;
72
import org.dataone.service.types.v1.ObjectFormatIdentifier;
73
import org.dataone.service.types.v1.ObjectList;
74
import org.dataone.service.types.v1.Permission;
75
import org.dataone.service.types.v1.Subject;
76
import org.dataone.service.types.v1.SystemMetadata;
77 6469 leinfelder
import org.dataone.service.util.DateTimeMarshaller;
78 6472 leinfelder
import org.dataone.service.util.ExceptionHandler;
79 6367 leinfelder
import org.dataone.service.util.TypeMarshaller;
80 6264 leinfelder
import org.jibx.runtime.JiBXException;
81 6472 leinfelder
import org.xml.sax.SAXException;
82 6264 leinfelder
83
import edu.ucsb.nceas.metacat.dataone.MNodeService;
84
85
/**
86
 * MN REST service implementation handler
87
 *
88
 * ******************
89
 * MNCore -- DONE
90
 * 		ping() - GET /d1/mn/monitor/ping
91
 * 		log() - GET /d1/mn/log
92
 * 		**getObjectStatistics() - GET /d1/mn/monitor/object
93
 * 		getOperationsStatistics - GET /d1/mn/monitor/event
94
 * 		**getStatus - GET /d1/mn/monitor/status
95
 * 		getCapabilities() - GET /d1/mn/ and /d1/mn/node
96
 *
97
 * 	MNRead -- DONE
98
 * 		get() - GET /d1/mn/object/PID
99
 * 		getSystemMetadata() - GET /d1/mn/meta/PID
100
 * 		describe() - HEAD /d1/mn/object/PID
101
 * 		getChecksum() - GET /d1/mn/checksum/PID
102
 * 		listObjects() - GET /d1/mn/object
103
 * 		synchronizationFailed() - POST /d1/mn/error
104
 *
105
 * 	MNAuthorization -- DONE
106
 * 		isAuthorized() - GET /d1/mn/isAuthorized/PID
107
 * 		setAccessPolicy() - PUT /d1/mn/accessRules/PID
108
 *
109 6297 cjones
 * 	MNStorage - DONE
110 6264 leinfelder
 * 		create() - POST /d1/mn/object/PID
111
 * 		update() - PUT /d1/mn/object/PID
112
 * 		delete() - DELETE /d1/mn/object/PID
113
 *
114
 * 	MNReplication
115
 * 		replicate() - POST /d1/mn/replicate
116
 *
117
 * ******************
118
 * @author leinfelder
119
 *
120
 */
121 6267 leinfelder
public class MNResourceHandler extends D1ResourceHandler{
122 6264 leinfelder
123
    // MN-specific API Resources
124
    protected static final String RESOURCE_MONITOR = "monitor";
125
    protected static final String RESOURCE_REPLICATE = "replicate";
126
    protected static final String RESOURCE_NODE = "node";
127
    protected static final String RESOURCE_ERROR = "error";
128
129
    /**
130
     * Initializes new instance by setting servlet context,request and response
131
     * */
132
    public MNResourceHandler(ServletContext servletContext,
133
            HttpServletRequest request, HttpServletResponse response) {
134
    	super(servletContext, request, response);
135
        logMetacat = Logger.getLogger(MNResourceHandler.class);
136
    }
137
138
    /**
139 6271 leinfelder
     * This function is called from REST API servlet and handles each request to the servlet
140 6264 leinfelder
     *
141
     * @param httpVerb (GET, POST, PUT or DELETE)
142
     */
143 6271 leinfelder
    @Override
144 6264 leinfelder
    public void handle(byte httpVerb) {
145 6271 leinfelder
    	// prepare the handler
146
    	super.handle(httpVerb);
147
148 6264 leinfelder
        try {
149 6284 leinfelder
        	// get the resource
150
            String resource = request.getPathInfo();
151
            resource = resource.substring(resource.indexOf("/") + 1);
152 6264 leinfelder
153 6284 leinfelder
            // default to node info
154
            if (resource.equals("")) {
155 6264 leinfelder
                resource = RESOURCE_NODE;
156
            }
157 6284 leinfelder
158
            // get the rest of the path info
159
            String extra = null;
160 6264 leinfelder
161 6272 leinfelder
            logMetacat.debug("handling verb " + httpVerb + " request with resource '" + resource + "'");
162
            logMetacat.debug("resource: '" + resource + "'");
163 6264 leinfelder
            boolean status = false;
164
165
            if (resource != null) {
166
167 6284 leinfelder
                if (resource.startsWith(RESOURCE_NODE)) {
168 6264 leinfelder
                    // node response
169
                    node();
170
                    status = true;
171 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_ACCESS_RULES)) {
172 6515 leinfelder
                    if (httpVerb == PUT) {
173 6514 leinfelder
                    	// after the command
174
                        extra = parseTrailing(resource, RESOURCE_ACCESS_RULES);
175 6264 leinfelder
	                	// set the access rules
176 6514 leinfelder
	                    setAccess(extra);
177 6264 leinfelder
	                    status = true;
178 6272 leinfelder
	                    logMetacat.debug("done setting access");
179 6264 leinfelder
                    }
180 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
181 6264 leinfelder
                    if (httpVerb == GET) {
182 6514 leinfelder
                    	// after the command
183
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
184 6264 leinfelder
	                	// check the access rules
185 6284 leinfelder
	                    isAuthorized(extra);
186 6264 leinfelder
	                    status = true;
187 6272 leinfelder
	                    logMetacat.debug("done getting access");
188 6264 leinfelder
                    }
189 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_META)) {
190 6272 leinfelder
                    logMetacat.debug("Using resource 'meta'");
191 6264 leinfelder
                    // get
192
                    if (httpVerb == GET) {
193 6514 leinfelder
                    	// after the command
194
                        extra = parseTrailing(resource, RESOURCE_META);
195 6284 leinfelder
                        getSystemMetadataObject(extra);
196 6264 leinfelder
                        status = true;
197
                    }
198
199 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
200 6272 leinfelder
                    logMetacat.debug("Using resource 'object'");
201 6514 leinfelder
                    // after the command
202
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
203 6284 leinfelder
                    logMetacat.debug("objectId: " + extra);
204 6264 leinfelder
                    logMetacat.debug("verb:" + httpVerb);
205
206
                    if (httpVerb == GET) {
207 6284 leinfelder
                        getObject(extra);
208 6264 leinfelder
                        status = true;
209
                    } else if (httpVerb == POST) {
210 6284 leinfelder
                        putObject(extra, FUNCTION_NAME_INSERT);
211 6264 leinfelder
                        status = true;
212
                    } else if (httpVerb == PUT) {
213 6284 leinfelder
                        putObject(extra, FUNCTION_NAME_UPDATE);
214 6264 leinfelder
                        status = true;
215
                    } else if (httpVerb == DELETE) {
216 6284 leinfelder
                        deleteObject(extra);
217 6264 leinfelder
                        status = true;
218
                    } else if (httpVerb == HEAD) {
219 6284 leinfelder
                        describeObject(extra);
220 6264 leinfelder
                        status = true;
221
                    }
222
223 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_LOG)) {
224 6272 leinfelder
                    logMetacat.debug("Using resource 'log'");
225 6264 leinfelder
                    // handle log events
226
                    if (httpVerb == GET) {
227
                        getLog();
228
                        status = true;
229
                    }
230 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_CHECKSUM)) {
231 6272 leinfelder
                    logMetacat.debug("Using resource 'checksum'");
232 6264 leinfelder
                    // handle checksum requests
233
                    if (httpVerb == GET) {
234 6514 leinfelder
                    	// after the command
235
                        extra = parseTrailing(resource, RESOURCE_CHECKSUM);
236 6284 leinfelder
                        checksum(extra);
237 6264 leinfelder
                        status = true;
238
                    }
239
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
240
                    // there are various parts to monitoring
241
                    if (httpVerb == GET) {
242 6514 leinfelder
                    	// after the command
243
                        extra = parseTrailing(resource, RESOURCE_MONITOR);
244 6264 leinfelder
                    	// health monitoring calls
245 6284 leinfelder
                        status = monitor(extra);
246 6264 leinfelder
                    }
247 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
248 6264 leinfelder
                	if (httpVerb == POST) {
249 6272 leinfelder
	                    logMetacat.debug("processing replicate request");
250 6264 leinfelder
	                    replicate();
251
	                    status = true;
252
                	}
253 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_ERROR)) {
254 6264 leinfelder
	                // sync error
255
	                if (httpVerb == POST) {
256
	                    syncError();
257
	                    status = true;
258
	                }
259
                }
260
261
                if (!status) {
262 6273 leinfelder
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
263 6264 leinfelder
                }
264
            } else {
265 6273 leinfelder
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
266 6264 leinfelder
            }
267
        } catch (BaseException be) {
268
        	// report Exceptions as clearly as possible
269
        	OutputStream out = null;
270
			try {
271
				out = response.getOutputStream();
272
			} catch (IOException e) {
273
				logMetacat.error("Could not get output stream from response", e);
274
			}
275
            serializeException(be, out);
276
        } catch (Exception e) {
277 6273 leinfelder
            // report Exceptions as clearly and generically as possible
278
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
279
        	OutputStream out = null;
280
			try {
281
				out = response.getOutputStream();
282
			} catch (IOException ioe) {
283
				logMetacat.error("Could not get output stream from response", ioe);
284
			}
285
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
286
            serializeException(se, out);
287 6264 leinfelder
        }
288
    }
289
290 6273 leinfelder
    /**
291
     * Checks the access policy
292
     * @param id
293
     * @return
294
     * @throws ServiceFailure
295
     * @throws InvalidToken
296
     * @throws NotFound
297
     * @throws NotAuthorized
298
     * @throws NotImplemented
299
     * @throws InvalidRequest
300
     */
301 6264 leinfelder
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
302
		Identifier pid = new Identifier();
303
		pid.setValue(id);
304
		Permission permission = null;
305
		try {
306 6509 leinfelder
			String perm = params.get("action")[0];
307 6373 leinfelder
			permission = Permission.convert(perm);
308 6264 leinfelder
		} catch (Exception e) {
309
			logMetacat.warn("No permission specified");
310
		}
311 6542 leinfelder
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
312 6264 leinfelder
		response.setStatus(200);
313
		response.setContentType("text/xml");
314
		return result;
315
    }
316
317 6273 leinfelder
    /**
318
     * Processes failed synchronization message
319
     * @throws NotImplemented
320
     * @throws ServiceFailure
321
     * @throws NotAuthorized
322
     * @throws InvalidRequest
323
     * @throws JiBXException
324 6367 leinfelder
     * @throws IllegalAccessException
325
     * @throws InstantiationException
326
     * @throws IOException
327 6273 leinfelder
     */
328 6367 leinfelder
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
329 6264 leinfelder
    	SynchronizationFailed syncFailed = null;
330 6472 leinfelder
		try {
331
			syncFailed = collectSynchronizationFailed();
332
		} catch (ParserConfigurationException e) {
333
			throw new ServiceFailure("2161", e.getMessage());
334
		} catch (SAXException e) {
335
			throw new ServiceFailure("2161", e.getMessage());
336
		}
337
338 6542 leinfelder
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
339 6264 leinfelder
    }
340
341 6472 leinfelder
342 6273 leinfelder
    /**
343
     * Handles the monitoring resources
344
     * @return
345
     * @throws NotFound
346
     * @throws ParseException
347
     * @throws NotImplemented
348
     * @throws ServiceFailure
349
     * @throws NotAuthorized
350
     * @throws InvalidRequest
351
     * @throws InsufficientResources
352
     * @throws UnsupportedType
353
     * @throws IOException
354
     * @throws JiBXException
355
     */
356 6297 cjones
    private boolean monitor(String pathInfo)
357
      throws NotFound, ParseException, NotImplemented, ServiceFailure,
358
      NotAuthorized, InvalidRequest, InsufficientResources, UnsupportedType,
359
      IOException, JiBXException {
360 6272 leinfelder
    	logMetacat.debug("processing monitor request");
361 6264 leinfelder
362 6272 leinfelder
        logMetacat.debug("verb is GET");
363
        logMetacat.debug("pathInfo is " + pathInfo);
364 6264 leinfelder
365
        if (pathInfo.toLowerCase().equals("ping")) {
366 6272 leinfelder
            logMetacat.debug("processing ping request");
367 6542 leinfelder
            boolean result = MNodeService.getInstance(request).ping();
368 6264 leinfelder
            return result;
369
370
        } else if (pathInfo.toLowerCase().equals("status")) {
371 6272 leinfelder
            logMetacat.debug("processing status request");
372 6264 leinfelder
            // TODO: implement in MNCore
373
            //MNodeService.getInstance().getStatus();
374
            return false;
375
376
        } else if (pathInfo.toLowerCase().equals("object")) {
377 6272 leinfelder
            logMetacat.debug("processing object request");
378 6264 leinfelder
            Identifier pid = null;
379
            ObjectFormat format = null;
380
            if (params.containsKey("format")) {
381
                String f = params.get("format")[0];
382
                format = ObjectFormatCache.getInstance().getFormat(f);
383
            }
384
            if (params.containsKey("pid")) {
385
                String id = params.get("pid")[0];
386
                pid = new Identifier();
387
                pid.setValue(id);
388
            }
389
390
            // TODO: implement in MNCore
391
            //ObjectStatistics objectStats = MNodeService.getInstance().getObjectStatistics(format, pid);
392 6274 leinfelder
            return false;
393 6264 leinfelder
394
        } else if (pathInfo.toLowerCase().equals("event")) {
395 6272 leinfelder
            logMetacat.debug("processing event request");
396 6297 cjones
            ObjectFormatIdentifier fmtid = null;
397
            String fromDateStr = null;
398
            Date fromDate = null;
399
            String toDateStr = null;
400
            Date toDate = null;
401 6264 leinfelder
            String requestor = null;
402
            Subject subject = null;
403
            String eventName = null;
404
            Event event = null;
405
406 6297 cjones
            if (params.containsKey("formatId")) {
407
                String f = params.get("formatId")[0];
408 6561 leinfelder
                fmtid = ObjectFormatCache.getInstance().getFormat(f).getFormatId();
409 6264 leinfelder
            }
410 6297 cjones
411
            if (params.containsKey("fromDate")) {
412
                fromDateStr = params.get("fromDate")[0];
413
                fromDate = getDateAsUTC(fromDateStr);
414 6264 leinfelder
            }
415 6297 cjones
416
            if (params.containsKey("toDate")) {
417
              toDateStr = params.get("toDate")[0];
418
              toDate = getDateAsUTC(toDateStr);
419
            }
420
421 6264 leinfelder
            if (params.containsKey("requestor")) {
422
            	requestor = params.get("requestor")[0];
423
            	subject = new Subject();
424
            	subject.setValue(requestor);
425
            }
426 6297 cjones
427 6264 leinfelder
            if (params.containsKey("event")) {
428
            	eventName = params.get("event")[0];
429 6373 leinfelder
                event = Event.convert(eventName);
430 6264 leinfelder
            }
431
432 6542 leinfelder
            MonitorList monitorList = MNodeService.getInstance(request).getOperationStatistics(session, fromDate, toDate, subject, event, fmtid);
433 6264 leinfelder
434
            OutputStream out = response.getOutputStream();
435
            response.setStatus(200);
436
            response.setContentType("text/xml");
437
438 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(monitorList, out);
439 6264 leinfelder
440 6274 leinfelder
            return true;
441
442 6264 leinfelder
        }
443
444 6274 leinfelder
        return false;
445 6264 leinfelder
    }
446
447 6297 cjones
    /*
448
     * Parse an ISO8601 date string, returning a Date in UTC time if the string
449
     * ends in 'Z'.
450
     *
451
     * @param fromDateStr -  the date string to be parsed
452
     * @return date -  the date object represented by the string
453
     */
454
    private Date getDateAsUTC(String fromDateStr)
455
      throws ParseException {
456
457
    	Date date = null;
458
459
    	try {
460
    		// try the expected date format
461
        // a date format for date string arguments
462
        DateFormat dateFormat =
463
        	new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
464
465
	      date = dateFormat.parse(fromDateStr);
466
467
    	} catch (ParseException e) {
468
    		// try the date with the UTC indicator
469
        DateFormat utcDateFormat =
470
        	new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
471
        utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
472
        date = utcDateFormat.parse(fromDateStr);
473
474
      }
475
476
    	return date;
477
    }
478
479
		/**
480 6273 leinfelder
     * Calculate the checksum
481
     * @throws NotImplemented
482
     * @throws JiBXException
483
     * @throws IOException
484
     * @throws InvalidToken
485
     * @throws ServiceFailure
486
     * @throws NotAuthorized
487
     * @throws NotFound
488
     * @throws InvalidRequest
489
     */
490 6285 cjones
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
491 6264 leinfelder
    	String checksumAlgorithm = "MD5";
492 6284 leinfelder
493 6285 cjones
        Identifier pidid = new Identifier();
494
        pidid.setValue(pid);
495 6264 leinfelder
        try {
496
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
497
        } catch(Exception e) {
498
            //do nothing.  default to MD5
499
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
500
        }
501 6285 cjones
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
502 6273 leinfelder
503 6542 leinfelder
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
504 6273 leinfelder
        logMetacat.debug("got checksum " + c.getValue());
505
        response.setStatus(200);
506
        logMetacat.debug("serializing response");
507 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
508 6273 leinfelder
        logMetacat.debug("done serializing response.");
509
510 6264 leinfelder
    }
511
512
	/**
513
     * handle the replicate action for MN
514 6273 leinfelder
	 * @throws JiBXException
515
	 * @throws FileUploadException
516
	 * @throws IOException
517
	 * @throws InvalidRequest
518
	 * @throws ServiceFailure
519
	 * @throws UnsupportedType
520
	 * @throws InsufficientResources
521
	 * @throws NotAuthorized
522
	 * @throws NotImplemented
523 6367 leinfelder
	 * @throws IllegalAccessException
524
	 * @throws InstantiationException
525 6264 leinfelder
     */
526 6367 leinfelder
    private void replicate() throws ServiceFailure, InvalidRequest, IOException, FileUploadException, JiBXException, NotImplemented, NotAuthorized, InsufficientResources, UnsupportedType, InstantiationException, IllegalAccessException {
527 6264 leinfelder
528 6272 leinfelder
        logMetacat.debug("in POST replicate()");
529 6264 leinfelder
530 6273 leinfelder
        //parse the systemMetadata
531
        SystemMetadata sysmeta = collectSystemMetadata();
532 6264 leinfelder
533 6273 leinfelder
        String sn = multipartparams.get("sourceNode").get(0);
534 6272 leinfelder
        logMetacat.debug("sourceNode: " + sn);
535 6548 cjones
        NodeReference sourceNode = new NodeReference();
536
        sourceNode.setValue(sn);
537 6542 leinfelder
        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
538 6264 leinfelder
539
        response.setStatus(200);
540
541
    }
542
543
    /**
544
     * Get the Node information
545
     *
546
     * @throws JiBXException
547
     * @throws IOException
548
     * @throws InvalidRequest
549
     * @throws ServiceFailure
550
     * @throws NotAuthorized
551
     * @throws NotImplemented
552
     */
553
    private void node()
554
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
555
556 6542 leinfelder
        Node n = MNodeService.getInstance(request).getCapabilities();
557 6264 leinfelder
558
        response.setContentType("text/xml");
559
        response.setStatus(200);
560 6501 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
561 6264 leinfelder
562
    }
563
564
    /**
565
     * MN_crud.describe()
566
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
567 6285 cjones
     * @param pid
568 6273 leinfelder
     * @throws InvalidRequest
569
     * @throws NotImplemented
570
     * @throws NotFound
571
     * @throws NotAuthorized
572
     * @throws ServiceFailure
573
     * @throws InvalidToken
574 6264 leinfelder
     */
575 6285 cjones
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
576 6264 leinfelder
    {
577
        response.setStatus(200);
578
        response.setContentType("text/xml");
579 6273 leinfelder
580 6264 leinfelder
        Identifier id = new Identifier();
581 6285 cjones
        id.setValue(pid);
582 6273 leinfelder
583 6542 leinfelder
        DescribeResponse dr = MNodeService.getInstance(request).describe(session, id);
584 6502 leinfelder
        //response.addHeader("pid", pid);
585 6507 leinfelder
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
586 6502 leinfelder
        response.addHeader("Content-Length", dr.getContent_Length() + "");
587
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
588
        response.addHeader("DataONE-fmtid", dr.getDataONE_ObjectFormatIdentifier().getValue());
589
590 6264 leinfelder
    }
591
592
    /**
593
     * get the logs based on passed params.  Available
594
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
595
     * for more info
596 6273 leinfelder
     * @throws NotImplemented
597
     * @throws InvalidRequest
598
     * @throws NotAuthorized
599
     * @throws ServiceFailure
600
     * @throws InvalidToken
601
     * @throws IOException
602
     * @throws JiBXException
603 6264 leinfelder
     */
604 6273 leinfelder
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
605 6264 leinfelder
    {
606 6273 leinfelder
607
        Date fromDate = null;
608
        Date toDate = null;
609
        Event event = null;
610
        Integer start = null;
611
        Integer count = null;
612
613 6264 leinfelder
        try {
614 6273 leinfelder
        	String fromDateS = params.get("fromDate")[0];
615
            logMetacat.debug("param fromDateS: " + fromDateS);
616 6469 leinfelder
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
617 6273 leinfelder
        } catch (Exception e) {
618
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
619 6264 leinfelder
        }
620 6273 leinfelder
        try {
621
        	String toDateS = params.get("toDate")[0];
622
            logMetacat.debug("param toDateS: " + toDateS);
623 6469 leinfelder
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
624 6273 leinfelder
        } catch (Exception e) {
625
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
626
		}
627
        try {
628
        	String eventS = params.get("event")[0];
629
            event = Event.convert(eventS);
630
        } catch (Exception e) {
631
        	logMetacat.warn("Could not parse event: " + e.getMessage());
632
		}
633
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
634
635
        try {
636
        	start =  Integer.parseInt(params.get("start")[0]);
637
        } catch (Exception e) {
638
			logMetacat.warn("Could not parse start: " + e.getMessage());
639
		}
640
        try {
641
        	count =  Integer.parseInt(params.get("count")[0]);
642
        } catch (Exception e) {
643
			logMetacat.warn("Could not parse count: " + e.getMessage());
644
		}
645
646
        logMetacat.debug("calling getLogRecords");
647 6542 leinfelder
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, start, count);
648 6273 leinfelder
649
        OutputStream out = response.getOutputStream();
650
        response.setStatus(200);
651
        response.setContentType("text/xml");
652
653 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(log, out);
654 6273 leinfelder
655 6264 leinfelder
    }
656
657
    /**
658
     * Implements REST version of DataONE CRUD API --> get
659 6285 cjones
     * @param pid ID of data object to be read
660 6273 leinfelder
     * @throws NotImplemented
661
     * @throws InvalidRequest
662
     * @throws NotFound
663
     * @throws NotAuthorized
664
     * @throws ServiceFailure
665
     * @throws InvalidToken
666
     * @throws IOException
667
     * @throws JiBXException
668 6264 leinfelder
     */
669 6285 cjones
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
670 6264 leinfelder
        OutputStream out = null;
671 6273 leinfelder
672 6285 cjones
        if (pid != null) { //get a specific document
673 6273 leinfelder
            Identifier id = new Identifier();
674 6285 cjones
            id.setValue(pid);
675 6273 leinfelder
676 6542 leinfelder
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
677 6264 leinfelder
678 6273 leinfelder
            //set the content type
679 6561 leinfelder
            if (sm.getFormatId().getValue().trim().equals(
680
            		ObjectFormatCache.getInstance().getFormat("text/csv").getFormatId().getValue()))
681 6273 leinfelder
            {
682
                response.setContentType("text/csv");
683
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".csv");
684
            }
685 6561 leinfelder
            else if (sm.getFormatId().getValue().trim().equals(
686
            		ObjectFormatCache.getInstance().getFormat("text/plain").getFormatId().getValue()))
687 6273 leinfelder
            {
688
                response.setContentType("text/plain");
689
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".txt");
690
            }
691 6561 leinfelder
            else if (sm.getFormatId().getValue().trim().equals(
692
            		ObjectFormatCache.getInstance().getFormat("application/octet-stream").getFormatId().getValue()))
693 6273 leinfelder
            {
694
                response.setContentType("application/octet-stream");
695
            }
696
            else
697
            {
698
                response.setContentType("text/xml");
699
                response.setHeader("Content-Disposition", "inline; filename=" + id.getValue() + ".xml");
700
            }
701
702 6542 leinfelder
            InputStream data = MNodeService.getInstance(request).get(session, id);
703 6273 leinfelder
704
            out = response.getOutputStream();
705
            IOUtils.copyLarge(data, out);
706
707
        }
708
        else
709
        { //call listObjects with specified params
710
            Date startTime = null;
711
            Date endTime = null;
712
            ObjectFormat objectFormat = null;
713
            boolean replicaStatus = false;
714
            int start = 0;
715
            //TODO: make the max count into a const
716
            int count = 1000;
717
            Enumeration paramlist = request.getParameterNames();
718
            while (paramlist.hasMoreElements())
719
            { //parse the params and make the crud call
720
                String name = (String) paramlist.nextElement();
721
                String[] value = (String[])request.getParameterValues(name);
722
723
                if (name.equals("startTime") && value != null)
724 6264 leinfelder
                {
725 6273 leinfelder
                    try
726 6264 leinfelder
                    {
727 6273 leinfelder
                      //startTime = dateFormat.parse(value[0]);
728 6469 leinfelder
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
729
                        //startTime = parseDateAndConvertToGMT(value[0]);
730 6264 leinfelder
                    }
731 6273 leinfelder
                    catch(Exception e)
732
                    {  //if we can't parse it, just don't use the startTime param
733
                        logMetacat.warn("Could not parse startTime: " + value[0]);
734
                        startTime = null;
735 6264 leinfelder
                    }
736 6273 leinfelder
                }
737
                else if(name.equals("endTime") && value != null)
738
                {
739
                    try
740 6264 leinfelder
                    {
741 6273 leinfelder
                      //endTime = dateFormat.parse(value[0]);
742 6469 leinfelder
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
743
                        //endTime = parseDateAndConvertToGMT(value[0]);
744 6264 leinfelder
                    }
745 6273 leinfelder
                    catch(Exception e)
746
                    {  //if we can't parse it, just don't use the endTime param
747
                        logMetacat.warn("Could not parse endTime: " + value[0]);
748
                        endTime = null;
749
                    }
750 6264 leinfelder
                }
751 6273 leinfelder
                else if(name.equals("objectFormat") && value != null)
752 6264 leinfelder
                {
753 6273 leinfelder
                    objectFormat = ObjectFormatCache.getInstance().getFormat(value[0]);
754 6264 leinfelder
                }
755 6273 leinfelder
                else if(name.equals("replicaStatus") && value != null)
756 6264 leinfelder
                {
757 6273 leinfelder
                    if(value != null &&
758
                       value.length > 0 &&
759
                       (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
760
                    {
761
                        replicaStatus = true;
762
                    }
763 6264 leinfelder
                }
764 6273 leinfelder
                else if(name.equals("start") && value != null)
765 6264 leinfelder
                {
766 6273 leinfelder
                    start = new Integer(value[0]).intValue();
767 6264 leinfelder
                }
768 6273 leinfelder
                else if(name.equals("count") && value != null)
769 6264 leinfelder
                {
770 6273 leinfelder
                    count = new Integer(value[0]).intValue();
771 6264 leinfelder
                }
772
            }
773 6273 leinfelder
            //make the crud call
774
            logMetacat.debug("session: " + session + " startTime: " + startTime +
775
                    " endtime: " + endTime + " objectFormat: " +
776
                    objectFormat + " replicaStatus: " + replicaStatus +
777
                    " start: " + start + " count: " + count);
778
779 6432 leinfelder
            ObjectFormatIdentifier fmtid = null;
780 6352 cjones
781
            if ( objectFormat != null ) {
782 6561 leinfelder
          	 fmtid = objectFormat.getFormatId();
783 6352 cjones
784
            }
785 6273 leinfelder
            ObjectList ol =
786 6542 leinfelder
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime,
787 6352 cjones
               fmtid, replicaStatus, start, count);
788
789 6273 leinfelder
            out = response.getOutputStream();
790
            response.setStatus(200);
791
            response.setContentType("text/xml");
792
            // Serialize and write it to the output stream
793 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
794 6273 leinfelder
795 6264 leinfelder
        }
796 6273 leinfelder
797 6264 leinfelder
    }
798
799 6273 leinfelder
800 6264 leinfelder
    /**
801 6273 leinfelder
     * Retrieve System Metadata
802 6285 cjones
     * @param pid
803 6273 leinfelder
     * @throws InvalidToken
804
     * @throws ServiceFailure
805
     * @throws NotAuthorized
806
     * @throws NotFound
807
     * @throws InvalidRequest
808
     * @throws NotImplemented
809
     * @throws IOException
810
     * @throws JiBXException
811 6264 leinfelder
     */
812 6285 cjones
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
813 6273 leinfelder
814
        Identifier id = new Identifier();
815 6285 cjones
        id.setValue(pid);
816 6542 leinfelder
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
817 6273 leinfelder
818
        response.setContentType("text/xml");
819
        response.setStatus(200);
820
        OutputStream out = response.getOutputStream();
821
822
        // Serialize and write it to the output stream
823 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
824 6273 leinfelder
   }
825 6264 leinfelder
826
827
    /**
828
     * Inserts or updates the object
829
     *
830 6285 cjones
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
831 6264 leinfelder
     *               is the existing pid.  If insert, the pid is the new one
832 6269 leinfelder
     * @throws InvalidRequest
833
     * @throws ServiceFailure
834
     * @throws JiBXException
835
     * @throws NotImplemented
836
     * @throws InvalidSystemMetadata
837
     * @throws InsufficientResources
838
     * @throws UnsupportedType
839
     * @throws IdentifierNotUnique
840
     * @throws NotAuthorized
841
     * @throws InvalidToken
842
     * @throws NotFound
843 6273 leinfelder
     * @throws IOException
844 6367 leinfelder
     * @throws IllegalAccessException
845
     * @throws InstantiationException
846 6264 leinfelder
     */
847 6367 leinfelder
    protected void putObject(String pid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
848 6272 leinfelder
        logMetacat.debug("putObject with pid " + pid);
849 6264 leinfelder
        logMetacat.debug("Entering putObject: " + pid + "/" + action);
850 6269 leinfelder
851 6273 leinfelder
        response.setStatus(200);
852
        response.setContentType("text/xml");
853
        OutputStream out = response.getOutputStream();
854
855 6269 leinfelder
        // Read the incoming data from its Mime Multipart encoding
856
    	Map<String, File> files = collectMultipartFiles();
857
        InputStream object = null;
858
        InputStream sysmeta = null;
859 6264 leinfelder
860 6269 leinfelder
        File smFile = files.get("sysmeta");
861
        sysmeta = new FileInputStream(smFile);
862
        File objFile = files.get("object");
863
        object = new FileInputStream(objFile);
864
865
        if (action.equals(FUNCTION_NAME_INSERT)) {
866
            // handle inserts
867
            logMetacat.debug("Commence creation...");
868 6367 leinfelder
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
869 6264 leinfelder
870 6269 leinfelder
            Identifier id = new Identifier();
871
            id.setValue(pid);
872 6272 leinfelder
            logMetacat.debug("creating object with pid " + pid);
873 6542 leinfelder
            Identifier rId = MNodeService.getInstance(request).create(session, id, object, smd);
874 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
875 6264 leinfelder
876 6269 leinfelder
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
877
        	// handle updates
878
879
            // construct pids
880
            Identifier newPid = new Identifier();
881
            try {
882
            	String newPidString = multipartparams.get("newPid").get(0);
883 6264 leinfelder
                newPid.setValue(newPidString);
884 6269 leinfelder
            } catch (Exception e) {
885
				logMetacat.warn("newPid not given");
886
			}
887
888
            Identifier obsoletedPid = new Identifier();
889
            obsoletedPid.setValue(pid);
890
891
            logMetacat.debug("Commence update...");
892
893
            // get the systemmetadata object
894 6367 leinfelder
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
895 6264 leinfelder
896 6542 leinfelder
            Identifier rId = MNodeService.getInstance(request).update(session, obsoletedPid, object, newPid, smd);
897 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
898 6269 leinfelder
        } else {
899
            throw new InvalidRequest("1000", "Operation must be create or update.");
900
        }
901 6264 leinfelder
902 6269 leinfelder
903 6264 leinfelder
    }
904
905
    /**
906
     * Handle delete
907 6285 cjones
     * @param pid ID of data object to be deleted
908 6264 leinfelder
     * @throws IOException
909 6273 leinfelder
     * @throws InvalidRequest
910
     * @throws NotImplemented
911
     * @throws NotFound
912
     * @throws NotAuthorized
913
     * @throws ServiceFailure
914
     * @throws InvalidToken
915
     * @throws JiBXException
916 6264 leinfelder
     */
917 6285 cjones
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException
918 6264 leinfelder
    {
919
920
        OutputStream out = response.getOutputStream();
921
        response.setStatus(200);
922
        response.setContentType("text/xml");
923
924
        Identifier id = new Identifier();
925 6285 cjones
        id.setValue(pid);
926 6273 leinfelder
927
        logMetacat.debug("Calling delete");
928 6542 leinfelder
        MNodeService.getInstance(request).delete(session, id);
929 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(id, out);
930 6273 leinfelder
931
    }
932 6264 leinfelder
933
    /**
934
     * set the access perms on a document
935 6273 leinfelder
     * @throws JiBXException
936
     * @throws InvalidRequest
937
     * @throws NotImplemented
938
     * @throws NotAuthorized
939
     * @throws NotFound
940
     * @throws ServiceFailure
941
     * @throws InvalidToken
942 6367 leinfelder
     * @throws IllegalAccessException
943
     * @throws InstantiationException
944
     * @throws IOException
945 6514 leinfelder
     * @throws SAXException
946
     * @throws ParserConfigurationException
947 6264 leinfelder
     */
948 6514 leinfelder
    protected void setAccess(String pid) throws JiBXException, InvalidToken, ServiceFailure, NotFound, NotAuthorized, NotImplemented, InvalidRequest, IOException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException
949 6264 leinfelder
    {
950 6273 leinfelder
951 6514 leinfelder
        //String pid = params.get("pid")[0];
952 6273 leinfelder
        Identifier id = new Identifier();
953 6285 cjones
        id.setValue(pid);
954 6514 leinfelder
955
        AccessPolicy accessPolicy = collectAccessPolicy();
956 6542 leinfelder
        MNodeService.getInstance(request).setAccessPolicy(session, id, accessPolicy);
957 6273 leinfelder
958
959 6264 leinfelder
    }
960
961 6514 leinfelder
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
962
963
		// Read the incoming data from its Mime Multipart encoding
964
		logMetacat.debug("Disassembling MIME multipart form");
965
		InputStream sf = null;
966
967
		// handle MMP inputs
968
		File tmpDir = getTempDirectory();
969
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
970
		MultipartRequestResolver mrr =
971
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
972
		MultipartRequest mr = null;
973
		try {
974
			mr = mrr.resolveMultipart(request);
975
		} catch (Exception e) {
976
			throw new ServiceFailure("2161",
977
					"Could not resolve multipart: " + e.getMessage());
978
		}
979
		logMetacat.debug("resolved multipart request");
980
		Map<String, File> files = mr.getMultipartFiles();
981
		if (files == null || files.keySet() == null) {
982
			throw new InvalidRequest("2163",
983
					"must have multipart file with name 'message'");
984
		}
985
		logMetacat.debug("got multipart files");
986
987
		multipartparams = mr.getMultipartParameters();
988
989
		File sfFile = files.get("message");
990
		if (sfFile == null) {
991
			throw new InvalidRequest("2163",
992
					"Missing the required file-part 'message' from the multipart request.");
993
		}
994
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
995
		sf = new FileInputStream(sfFile);
996
997
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
998
		return syncFailed;
999
	}
1000
1001 6264 leinfelder
}