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 8810 leinfelder
package edu.ucsb.nceas.metacat.restservice.v2;
24 6264 leinfelder
25
import java.io.File;
26
import java.io.FileInputStream;
27 9122 tao
import java.io.FileNotFoundException;
28 6264 leinfelder
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 9235 leinfelder
import java.util.Iterator;
34 6264 leinfelder
import java.util.Map;
35 6679 leinfelder
import java.util.concurrent.ExecutorService;
36
import java.util.concurrent.Executors;
37 6264 leinfelder
38
import javax.servlet.ServletContext;
39
import javax.servlet.http.HttpServletRequest;
40
import javax.servlet.http.HttpServletResponse;
41 6472 leinfelder
import javax.xml.parsers.ParserConfigurationException;
42 6264 leinfelder
43 6273 leinfelder
import org.apache.commons.fileupload.FileUploadException;
44 6264 leinfelder
import org.apache.commons.io.IOUtils;
45
import org.apache.log4j.Logger;
46 9235 leinfelder
import org.dataone.client.v2.formats.ObjectFormatCache;
47 8810 leinfelder
import org.dataone.client.v2.formats.ObjectFormatInfo;
48 6472 leinfelder
import org.dataone.mimemultipart.MultipartRequest;
49
import org.dataone.mimemultipart.MultipartRequestResolver;
50 8874 leinfelder
import org.dataone.portal.TokenGenerator;
51 6264 leinfelder
import org.dataone.service.exceptions.BaseException;
52
import org.dataone.service.exceptions.IdentifierNotUnique;
53
import org.dataone.service.exceptions.InsufficientResources;
54
import org.dataone.service.exceptions.InvalidRequest;
55
import org.dataone.service.exceptions.InvalidSystemMetadata;
56
import org.dataone.service.exceptions.InvalidToken;
57
import org.dataone.service.exceptions.NotAuthorized;
58
import org.dataone.service.exceptions.NotFound;
59
import org.dataone.service.exceptions.NotImplemented;
60
import org.dataone.service.exceptions.ServiceFailure;
61
import org.dataone.service.exceptions.SynchronizationFailed;
62
import org.dataone.service.exceptions.UnsupportedType;
63 6366 leinfelder
import org.dataone.service.types.v1.Checksum;
64
import org.dataone.service.types.v1.DescribeResponse;
65
import org.dataone.service.types.v1.Identifier;
66
import org.dataone.service.types.v1.NodeReference;
67
import org.dataone.service.types.v1.ObjectFormatIdentifier;
68
import org.dataone.service.types.v1.ObjectList;
69
import org.dataone.service.types.v1.Permission;
70 9009 leinfelder
import org.dataone.service.types.v1.Person;
71 9173 leinfelder
import org.dataone.service.types.v1.Subject;
72
import org.dataone.service.types.v1.SubjectInfo;
73 7417 leinfelder
import org.dataone.service.types.v1_1.QueryEngineDescription;
74
import org.dataone.service.types.v1_1.QueryEngineList;
75 8874 leinfelder
import org.dataone.service.types.v2.Log;
76 9235 leinfelder
import org.dataone.service.types.v2.MediaType;
77
import org.dataone.service.types.v2.MediaTypeProperty;
78 8874 leinfelder
import org.dataone.service.types.v2.Node;
79 9148 tao
import org.dataone.service.types.v2.OptionList;
80 8874 leinfelder
import org.dataone.service.types.v2.SystemMetadata;
81 7019 leinfelder
import org.dataone.service.util.Constants;
82 6469 leinfelder
import org.dataone.service.util.DateTimeMarshaller;
83 6472 leinfelder
import org.dataone.service.util.ExceptionHandler;
84 6367 leinfelder
import org.dataone.service.util.TypeMarshaller;
85 6264 leinfelder
import org.jibx.runtime.JiBXException;
86 6472 leinfelder
import org.xml.sax.SAXException;
87 6264 leinfelder
88 8874 leinfelder
import edu.ucsb.nceas.metacat.MetaCatServlet;
89 7757 leinfelder
import edu.ucsb.nceas.metacat.common.query.stream.ContentTypeInputStream;
90 6264 leinfelder
import edu.ucsb.nceas.metacat.dataone.MNodeService;
91 6994 leinfelder
import edu.ucsb.nceas.metacat.properties.PropertyService;
92 8810 leinfelder
import edu.ucsb.nceas.metacat.restservice.D1ResourceHandler;
93 8160 leinfelder
import edu.ucsb.nceas.metacat.util.DeleteOnCloseFileInputStream;
94 6994 leinfelder
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
95 6264 leinfelder
96
/**
97
 * MN REST service implementation handler
98
 *
99
 * ******************
100
 * MNCore -- DONE
101
 * 		ping() - GET /d1/mn/monitor/ping
102
 * 		log() - GET /d1/mn/log
103
 * 		**getObjectStatistics() - GET /d1/mn/monitor/object
104
 * 		getOperationsStatistics - GET /d1/mn/monitor/event
105
 * 		**getStatus - GET /d1/mn/monitor/status
106
 * 		getCapabilities() - GET /d1/mn/ and /d1/mn/node
107
 *
108
 * 	MNRead -- DONE
109
 * 		get() - GET /d1/mn/object/PID
110
 * 		getSystemMetadata() - GET /d1/mn/meta/PID
111
 * 		describe() - HEAD /d1/mn/object/PID
112
 * 		getChecksum() - GET /d1/mn/checksum/PID
113
 * 		listObjects() - GET /d1/mn/object
114
 * 		synchronizationFailed() - POST /d1/mn/error
115
 *
116
 * 	MNAuthorization -- DONE
117
 * 		isAuthorized() - GET /d1/mn/isAuthorized/PID
118
 * 		setAccessPolicy() - PUT /d1/mn/accessRules/PID
119
 *
120 6297 cjones
 * 	MNStorage - DONE
121 6264 leinfelder
 * 		create() - POST /d1/mn/object/PID
122
 * 		update() - PUT /d1/mn/object/PID
123
 * 		delete() - DELETE /d1/mn/object/PID
124 7153 leinfelder
 * 		archive() - PUT /d1/mn/archive/PID
125 9122 tao
 *      updateSystemMetadata() - PUT /d1/mn/meta
126 7153 leinfelder
127 6619 cjones
 *    systemMetadataChanged() - POST /dirtySystemMetadata/PID
128 6264 leinfelder
 *
129
 * 	MNReplication
130
 * 		replicate() - POST /d1/mn/replicate
131 6604 cjones
 *    getReplica() - GET /d1/mn/replica
132 6264 leinfelder
 *
133
 * ******************
134
 * @author leinfelder
135
 *
136
 */
137 7417 leinfelder
public class MNResourceHandler extends D1ResourceHandler {
138 6264 leinfelder
139
    // MN-specific API Resources
140
    protected static final String RESOURCE_MONITOR = "monitor";
141
    protected static final String RESOURCE_REPLICATE = "replicate";
142 6604 cjones
    protected static final String RESOURCE_REPLICAS = "replica";
143 6264 leinfelder
    protected static final String RESOURCE_NODE = "node";
144
    protected static final String RESOURCE_ERROR = "error";
145 6619 cjones
    protected static final String RESOURCE_META_CHANGED = "dirtySystemMetadata";
146 7441 leinfelder
    protected static final String RESOURCE_GENERATE_ID = "generate";
147 7864 leinfelder
    protected static final String RESOURCE_PUBLISH = "publish";
148 7855 leinfelder
    protected static final String RESOURCE_PACKAGE = "package";
149 8874 leinfelder
    protected static final String RESOURCE_TOKEN = "token";
150 9173 leinfelder
    protected static final String RESOURCE_WHOAMI = "whoami";
151 7441 leinfelder
152 7861 leinfelder
153 8874 leinfelder
154 7143 leinfelder
155
    // shared executor
156
	private static ExecutorService executor = null;
157 6264 leinfelder
158 7143 leinfelder
	static {
159
		// use a shared executor service with nThreads == one less than available processors
160
    	int availableProcessors = Runtime.getRuntime().availableProcessors();
161
        int nThreads = availableProcessors * 1;
162
        nThreads--;
163 7183 leinfelder
        nThreads = Math.max(1, nThreads);
164 7143 leinfelder
    	executor = Executors.newFixedThreadPool(nThreads);
165
	}
166
167 6264 leinfelder
    /**
168
     * Initializes new instance by setting servlet context,request and response
169
     * */
170
    public MNResourceHandler(ServletContext servletContext,
171
            HttpServletRequest request, HttpServletResponse response) {
172
    	super(servletContext, request, response);
173
        logMetacat = Logger.getLogger(MNResourceHandler.class);
174
    }
175 6994 leinfelder
176
    @Override
177
    protected boolean isD1Enabled() {
178
179
    	boolean enabled = false;
180
    	try {
181
			enabled = Boolean.parseBoolean(PropertyService.getProperty("dataone.mn.services.enabled"));
182
		} catch (PropertyNotFoundException e) {
183
			logMetacat.error("Could not check if DataONE is enabled: " + e.getMessage());
184
		}
185
186
    	return enabled;
187
    }
188 6264 leinfelder
189
    /**
190 6271 leinfelder
     * This function is called from REST API servlet and handles each request to the servlet
191 6264 leinfelder
     *
192
     * @param httpVerb (GET, POST, PUT or DELETE)
193
     */
194 6271 leinfelder
    @Override
195 6264 leinfelder
    public void handle(byte httpVerb) {
196 6271 leinfelder
    	// prepare the handler
197
    	super.handle(httpVerb);
198
199 6264 leinfelder
        try {
200 6994 leinfelder
201
        	// only service requests if we have D1 configured
202
        	if (!isD1Enabled()) {
203
        		ServiceFailure se = new ServiceFailure("0000", "DataONE services are not enabled on this node");
204
                serializeException(se, response.getOutputStream());
205
                return;
206
        	}
207
208 6284 leinfelder
        	// get the resource
209
            String resource = request.getPathInfo();
210
            resource = resource.substring(resource.indexOf("/") + 1);
211 6264 leinfelder
212 6284 leinfelder
            // default to node info
213
            if (resource.equals("")) {
214 6264 leinfelder
                resource = RESOURCE_NODE;
215
            }
216 6284 leinfelder
217
            // get the rest of the path info
218
            String extra = null;
219 6264 leinfelder
220 6272 leinfelder
            logMetacat.debug("handling verb " + httpVerb + " request with resource '" + resource + "'");
221
            logMetacat.debug("resource: '" + resource + "'");
222 6264 leinfelder
            boolean status = false;
223
224
            if (resource != null) {
225
226 6284 leinfelder
                if (resource.startsWith(RESOURCE_NODE)) {
227 6264 leinfelder
                    // node response
228
                    node();
229
                    status = true;
230 8874 leinfelder
                } else if (resource.startsWith(RESOURCE_TOKEN)) {
231
                    logMetacat.debug("Using resource 'token'");
232
                    // get
233
                    if (httpVerb == GET) {
234
                    	// after the command
235
                        getToken();
236
                        status = true;
237
                    }
238
239 9173 leinfelder
                } else if (resource.startsWith(RESOURCE_WHOAMI)) {
240
                    logMetacat.debug("Using resource 'whoami'");
241
                    // get
242
                    if (httpVerb == GET) {
243
                    	// after the command
244
                        whoami();
245
                        status = true;
246
                    }
247
248 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_IS_AUTHORIZED)) {
249 6264 leinfelder
                    if (httpVerb == GET) {
250 6514 leinfelder
                    	// after the command
251
                        extra = parseTrailing(resource, RESOURCE_IS_AUTHORIZED);
252 6264 leinfelder
	                	// check the access rules
253 6284 leinfelder
	                    isAuthorized(extra);
254 6264 leinfelder
	                    status = true;
255 6272 leinfelder
	                    logMetacat.debug("done getting access");
256 6264 leinfelder
                    }
257 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_META)) {
258 6272 leinfelder
                    logMetacat.debug("Using resource 'meta'");
259 6264 leinfelder
                    // get
260
                    if (httpVerb == GET) {
261 9122 tao
                        logMetacat.debug("Using resource 'meta' for GET");
262 6514 leinfelder
                    	// after the command
263
                        extra = parseTrailing(resource, RESOURCE_META);
264 6284 leinfelder
                        getSystemMetadataObject(extra);
265 6264 leinfelder
                        status = true;
266 9122 tao
                    } else if (httpVerb == PUT) {
267
                        logMetacat.debug("Using resource 'meta' for PUT");
268
                        updateSystemMetadata();
269
                        status = true;
270 6264 leinfelder
                    }
271
272 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_OBJECTS)) {
273 6272 leinfelder
                    logMetacat.debug("Using resource 'object'");
274 6514 leinfelder
                    // after the command
275
                    extra = parseTrailing(resource, RESOURCE_OBJECTS);
276 6284 leinfelder
                    logMetacat.debug("objectId: " + extra);
277 6264 leinfelder
                    logMetacat.debug("verb:" + httpVerb);
278
279
                    if (httpVerb == GET) {
280 6284 leinfelder
                        getObject(extra);
281 6264 leinfelder
                        status = true;
282
                    } else if (httpVerb == POST) {
283 6972 leinfelder
                    	// part of the params, not the URL
284
                        putObject(null, FUNCTION_NAME_INSERT);
285 6264 leinfelder
                        status = true;
286
                    } else if (httpVerb == PUT) {
287 6284 leinfelder
                        putObject(extra, FUNCTION_NAME_UPDATE);
288 6264 leinfelder
                        status = true;
289
                    } else if (httpVerb == DELETE) {
290 6284 leinfelder
                        deleteObject(extra);
291 6264 leinfelder
                        status = true;
292
                    } else if (httpVerb == HEAD) {
293 6284 leinfelder
                        describeObject(extra);
294 6264 leinfelder
                        status = true;
295
                    }
296
297 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_LOG)) {
298 6272 leinfelder
                    logMetacat.debug("Using resource 'log'");
299 6264 leinfelder
                    // handle log events
300
                    if (httpVerb == GET) {
301
                        getLog();
302
                        status = true;
303
                    }
304 7153 leinfelder
                } else if (resource.startsWith(Constants.RESOURCE_ARCHIVE)) {
305
                    logMetacat.debug("Using resource " + Constants.RESOURCE_ARCHIVE);
306
                    // handle archive events
307
                    if (httpVerb == PUT) {
308
                        extra = parseTrailing(resource, Constants.RESOURCE_ARCHIVE);
309
                        archive(extra);
310
                        status = true;
311
                    }
312 7019 leinfelder
                } else if (resource.startsWith(Constants.RESOURCE_CHECKSUM)) {
313 6272 leinfelder
                    logMetacat.debug("Using resource 'checksum'");
314 6264 leinfelder
                    // handle checksum requests
315
                    if (httpVerb == GET) {
316 6514 leinfelder
                    	// after the command
317 7019 leinfelder
                        extra = parseTrailing(resource, Constants.RESOURCE_CHECKSUM);
318 6284 leinfelder
                        checksum(extra);
319 6264 leinfelder
                        status = true;
320
                    }
321
                } else if (resource.startsWith(RESOURCE_MONITOR)) {
322
                    // there are various parts to monitoring
323
                    if (httpVerb == GET) {
324 6514 leinfelder
                    	// after the command
325
                        extra = parseTrailing(resource, RESOURCE_MONITOR);
326 6803 leinfelder
327
                        // ping
328
                        if (extra.toLowerCase().equals("ping")) {
329
                            logMetacat.debug("processing ping request");
330
                            Date result = MNodeService.getInstance(request).ping();
331
                            // TODO: send to output
332
                            status = true;
333
334
                        }
335
336 6264 leinfelder
                    }
337 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_REPLICATE)) {
338 6264 leinfelder
                	if (httpVerb == POST) {
339 6272 leinfelder
	                    logMetacat.debug("processing replicate request");
340 6264 leinfelder
	                    replicate();
341
	                    status = true;
342
                	}
343 6284 leinfelder
                } else if (resource.startsWith(RESOURCE_ERROR)) {
344 6264 leinfelder
	                // sync error
345
	                if (httpVerb == POST) {
346
	                    syncError();
347
	                    status = true;
348
	                }
349 6604 cjones
                } else if (resource.startsWith(RESOURCE_META_CHANGED)) {
350
                    // system metadata changed
351
                    if (httpVerb == POST) {
352 6650 leinfelder
                        systemMetadataChanged();
353 6604 cjones
                        status = true;
354
                    }
355
                } else if (resource.startsWith(RESOURCE_REPLICAS)) {
356
                    // get replica
357
                    if (httpVerb == GET) {
358
                        extra = parseTrailing(resource, RESOURCE_REPLICAS);
359
                        getReplica(extra);
360
                        status = true;
361
                    }
362 7417 leinfelder
                } else if (resource.startsWith(RESOURCE_QUERY)) {
363
	                logMetacat.debug("Using resource " + RESOURCE_QUERY);
364
	                // after the command
365
	                extra = parseTrailing(resource, RESOURCE_QUERY);
366
	                logMetacat.debug("query extra: " + extra);
367
368
	                String engine = null;
369
	                String query = null;
370
371
	                if (extra != null) {
372
		                // get the engine
373
		                int engineIndex = extra.length();
374
		                if (extra.indexOf("/") > -1) {
375
		                	engineIndex = extra.indexOf("/");
376
		                }
377
		                engine = extra.substring(0, engineIndex);
378
		                logMetacat.debug("query engine: " + engine);
379
380
		                // get the query if it is there
381
		                query = extra.substring(engineIndex, extra.length());
382
		                if (query != null && query.length() == 0) {
383
		                	query = null;
384
		                } else {
385
		                	if (query.startsWith("/")) {
386
		                		query = query.substring(1);
387
		                    }
388 8715 leinfelder
		                	// remove the query delimiter if it exists
389
		                	if (query.startsWith("?")) {
390
		                		query = query.substring(1);
391
		                    }
392 7417 leinfelder
		                }
393
		                logMetacat.debug("query: " + query);
394
395
	                }
396
	                logMetacat.debug("verb:" + httpVerb);
397
	                if (httpVerb == GET) {
398
	                    doQuery(engine, query);
399
	                    status = true;
400
	                }
401 7441 leinfelder
                } else if (resource.startsWith(RESOURCE_GENERATE_ID)) {
402
                	// generate an id
403
                    if (httpVerb == POST) {
404
                        generateIdentifier();
405
                        status = true;
406
                    }
407 7864 leinfelder
                } else if (resource.startsWith(RESOURCE_PUBLISH)) {
408
                    logMetacat.debug("Using resource: " + RESOURCE_PUBLISH);
409
                    // PUT
410
                    if (httpVerb == PUT) {
411
                    	// after the command
412
                        extra = parseTrailing(resource, RESOURCE_PUBLISH);
413
                        publish(extra);
414
                        status = true;
415
                    }
416 7855 leinfelder
                } else if (resource.startsWith(RESOURCE_PACKAGE)) {
417
                    logMetacat.debug("Using resource: " + RESOURCE_PACKAGE);
418 8810 leinfelder
                    // after the command
419
                    extra = parseTrailing(resource, RESOURCE_PACKAGE);
420
421
                    String format = null;
422
	                String pid = null;
423
424
	                if (extra != null) {
425
		                // get the format
426
		                int formatIndex = extra.length();
427
		                if (extra.indexOf("/") > -1) {
428
		                	formatIndex = extra.indexOf("/");
429
		                }
430
		                format = extra.substring(0, formatIndex);
431
		                logMetacat.debug("package format: " + format);
432
433
		                // get the pid if it is there
434
		                pid = extra.substring(formatIndex, extra.length());
435
		                if (pid != null && pid.length() == 0) {
436
		                	pid = null;
437
		                } else {
438
		                	if (pid.startsWith("/")) {
439
		                		pid = pid.substring(1);
440
		                    }
441
		                }
442
		                logMetacat.debug("pid: " + pid);
443
444
	                }
445
446 7855 leinfelder
                    // get
447
                    if (httpVerb == GET) {
448 8810 leinfelder
449
                        getPackage(format, pid);
450 7855 leinfelder
                        status = true;
451 7861 leinfelder
                    }
452
                } else if (resource.startsWith(RESOURCE_VIEWS)) {
453
	                logMetacat.debug("Using resource " + RESOURCE_VIEWS);
454
	                // after the command
455
	                extra = parseTrailing(resource, RESOURCE_VIEWS);
456
	                logMetacat.debug("view extra: " + extra);
457
458
	                String format = null;
459
	                String pid = null;
460
461
	                if (extra != null) {
462
		                // get the format
463
		                int formatIndex = extra.length();
464
		                if (extra.indexOf("/") > -1) {
465
		                	formatIndex = extra.indexOf("/");
466
		                }
467
		                format = extra.substring(0, formatIndex);
468
		                logMetacat.debug("view format: " + format);
469
470
		                // get the pid if it is there
471
		                pid = extra.substring(formatIndex, extra.length());
472
		                if (pid != null && pid.length() == 0) {
473
		                	pid = null;
474
		                } else {
475
		                	if (pid.startsWith("/")) {
476
		                		pid = pid.substring(1);
477
		                    }
478
		                }
479
		                logMetacat.debug("pid: " + pid);
480
481
	                }
482
	                logMetacat.debug("verb:" + httpVerb);
483
	                if (httpVerb == GET) {
484
	                    doViews(format, pid);
485
	                    status = true;
486
	                }
487 7855 leinfelder
                }
488 6264 leinfelder
489
                if (!status) {
490 6273 leinfelder
                	throw new ServiceFailure("0000", "Unknown error, status = " + status);
491 6264 leinfelder
                }
492
            } else {
493 6273 leinfelder
            	throw new InvalidRequest("0000", "No resource matched for " + resource);
494 6264 leinfelder
            }
495
        } catch (BaseException be) {
496
        	// report Exceptions as clearly as possible
497
        	OutputStream out = null;
498
			try {
499
				out = response.getOutputStream();
500
			} catch (IOException e) {
501
				logMetacat.error("Could not get output stream from response", e);
502
			}
503
            serializeException(be, out);
504
        } catch (Exception e) {
505 6273 leinfelder
            // report Exceptions as clearly and generically as possible
506
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
507
        	OutputStream out = null;
508
			try {
509
				out = response.getOutputStream();
510
			} catch (IOException ioe) {
511
				logMetacat.error("Could not get output stream from response", ioe);
512
			}
513
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
514
            serializeException(se, out);
515 6264 leinfelder
        }
516
    }
517
518 6604 cjones
519 7417 leinfelder
    private void doQuery(String engine, String query) {
520
521
		OutputStream out = null;
522
523
    	try {
524
    		// NOTE: we set the session explicitly for the MNode instance since these methods do not provide a parameter
525
	    	if (engine == null) {
526
	    		// just looking for list of engines
527
	    		MNodeService mnode = MNodeService.getInstance(request);
528
    			mnode.setSession(session);
529 8810 leinfelder
	    		QueryEngineList qel = mnode.listQueryEngines(session);
530 7417 leinfelder
	    		response.setContentType("text/xml");
531
	            response.setStatus(200);
532
	            out = response.getOutputStream();
533
	            TypeMarshaller.marshalTypeToOutputStream(qel, out);
534
	            return;
535
	    	} else {
536
	    		if (query != null) {
537
	    			MNodeService mnode = MNodeService.getInstance(request);
538
	    			mnode.setSession(session);
539 8810 leinfelder
	    			InputStream stream = mnode.query(session, engine, query);
540 7417 leinfelder
541 7757 leinfelder
	    			// set the content-type if we have it from the implementation
542
	    			if (stream instanceof ContentTypeInputStream) {
543
		    			//response.setContentType("application/octet-stream");
544
		    			//response.setContentType("text/xml");
545
	    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
546
	    			}
547 7417 leinfelder
	                response.setStatus(200);
548
	                out = response.getOutputStream();
549
	                // write the results to the output stream
550
	                IOUtils.copyLarge(stream, out);
551
	                return;
552
	    		} else {
553
	    			MNodeService mnode = MNodeService.getInstance(request);
554
	    			mnode.setSession(session);
555 8810 leinfelder
	    			QueryEngineDescription qed = mnode.getQueryEngineDescription(session, engine);
556 7417 leinfelder
	    			response.setContentType("text/xml");
557
		            response.setStatus(200);
558
		            out = response.getOutputStream();
559
		            TypeMarshaller.marshalTypeToOutputStream(qed, out);
560
		            return;
561
	    		}
562
	    	}
563
564
565
    	} catch (BaseException be) {
566
        	// report Exceptions as clearly as possible
567
			try {
568
				out = response.getOutputStream();
569
			} catch (IOException e) {
570
				logMetacat.error("Could not get output stream from response", e);
571
			}
572
            serializeException(be, out);
573
        } catch (Exception e) {
574
            // report Exceptions as clearly and generically as possible
575
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
576
			try {
577
				out = response.getOutputStream();
578
			} catch (IOException ioe) {
579
				logMetacat.error("Could not get output stream from response", ioe);
580
			}
581
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
582
            serializeException(se, out);
583
        }
584
    }
585
586 7861 leinfelder
    private void doViews(String format, String pid) {
587
588
		OutputStream out = null;
589
		MNodeService mnode = MNodeService.getInstance(request);
590
591
    	try {
592
    		// get a list of views
593
    		if (pid != null) {
594
    			Identifier identifier = new Identifier();
595
    			identifier.setValue(pid);
596 8810 leinfelder
				InputStream stream = mnode.view(session, format, identifier);
597 7861 leinfelder
598
    			// set the content-type if we have it from the implementation
599
    			if (stream instanceof ContentTypeInputStream) {
600
    				response.setContentType(((ContentTypeInputStream) stream).getContentType());
601
    			}
602
                response.setStatus(200);
603
                out = response.getOutputStream();
604
                // write the results to the output stream
605
                IOUtils.copyLarge(stream, out);
606
                return;
607
    		} else {
608
    			// TODO: list the registered views
609 9148 tao
                //BaseException ni = new NotImplemented("9999", "MN.listViews() is not implemented at this node");
610
				//throw ni;
611
    		    OptionList list = mnode.listViews(session);
612
613
    	        response.setContentType("text/xml");
614
    	        response.setStatus(200);
615
    	        TypeMarshaller.marshalTypeToOutputStream(list, response.getOutputStream());
616 7861 leinfelder
    		}
617
618
619
    	} catch (BaseException be) {
620
        	// report Exceptions as clearly as possible
621
			try {
622
				out = response.getOutputStream();
623
			} catch (IOException e) {
624
				logMetacat.error("Could not get output stream from response", e);
625
			}
626
            serializeException(be, out);
627
        } catch (Exception e) {
628
            // report Exceptions as clearly and generically as possible
629
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
630
			try {
631
				out = response.getOutputStream();
632
			} catch (IOException ioe) {
633
				logMetacat.error("Could not get output stream from response", ioe);
634
			}
635
			ServiceFailure se = new ServiceFailure("0000", e.getMessage());
636
            serializeException(se, out);
637
        }
638
    }
639 7417 leinfelder
640 6273 leinfelder
    /**
641 6604 cjones
     * Handles notification of system metadata changes for the given identifier
642
     *
643
     * @param id  the identifier for the object
644
     * @throws InvalidToken
645
     * @throws InvalidRequest
646
     * @throws NotAuthorized
647
     * @throws ServiceFailure
648
     * @throws NotImplemented
649
     */
650 6650 leinfelder
    private void systemMetadataChanged()
651 6604 cjones
        throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest,
652
        InvalidToken {
653
654
        long serialVersion = 0L;
655
        String serialVersionStr = null;
656
        Date dateSysMetaLastModified = null;
657
        String dateSysMetaLastModifiedStr = null;
658 6650 leinfelder
        Identifier pid = null;
659 6604 cjones
660 6660 leinfelder
        // mkae sure we have the multipart params
661
        try {
662
			initMultipartParams();
663
		} catch (Exception e1) {
664
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
665
		}
666
667 6650 leinfelder
        // get the pid
668
        try {
669 6660 leinfelder
        	String id = multipartparams.get("pid").get(0);
670 6650 leinfelder
        	pid = new Identifier();
671
            pid.setValue(id);
672
        } catch (NullPointerException e) {
673
            String msg = "The 'pid' must be provided as a parameter and was not.";
674
            logMetacat.error(msg);
675
            throw new InvalidRequest("1334", msg);
676
        }
677
678 6604 cjones
        // get the serialVersion
679
        try {
680 6660 leinfelder
            serialVersionStr = multipartparams.get("serialVersion").get(0);
681 6604 cjones
            serialVersion = new Long(serialVersionStr).longValue();
682
683
        } catch (NullPointerException e) {
684
            String msg = "The 'serialVersion' must be provided as a parameter and was not.";
685
            logMetacat.error(msg);
686
            throw new InvalidRequest("1334", msg);
687
688
        }
689
690
        // get the dateSysMetaLastModified
691
        try {
692 6660 leinfelder
            dateSysMetaLastModifiedStr = multipartparams.get("dateSysMetaLastModified").get(0);
693 6604 cjones
            dateSysMetaLastModified = DateTimeMarshaller.deserializeDateToUTC(dateSysMetaLastModifiedStr);
694
695
        } catch (NullPointerException e) {
696
            String msg =
697
                "The 'dateSysMetaLastModified' must be provided as a " +
698
                "parameter and was not, or was an invalid representation of the timestamp.";
699
            logMetacat.error(msg);
700
            throw new InvalidRequest("1334", msg);
701
702
        }
703
704
        // call the service
705
        MNodeService.getInstance(request).systemMetadataChanged(session, pid, serialVersion, dateSysMetaLastModified);
706
        response.setStatus(200);
707
    }
708 7441 leinfelder
709
    /**
710
     * Handles identifier generation calls
711
     *
712
     * @throws InvalidRequest
713
     * @throws NotImplemented
714
     * @throws NotAuthorized
715
     * @throws ServiceFailure
716
     * @throws InvalidToken
717
     * @throws IOException
718
     * @throws JiBXException
719
     */
720
    private void generateIdentifier() throws InvalidToken, ServiceFailure, NotAuthorized, NotImplemented, InvalidRequest, IOException, JiBXException {
721
722
        // make sure we have the multipart params
723
        try {
724
			initMultipartParams();
725
		} catch (Exception e1) {
726
			throw new ServiceFailure("1333", "Could not collect the multipart params for the request");
727
		}
728
729
        // get the scheme
730
		String scheme = null;
731
        try {
732
        	scheme = multipartparams.get("scheme").get(0);
733
        } catch (NullPointerException e) {
734
            String msg = "The 'scheme' parameter was not provided, using default";
735
            logMetacat.warn(msg);
736
        }
737
738
        // get the fragment
739
		String fragment = null;
740
        try {
741
        	fragment = multipartparams.get("fragment").get(0);
742
        } catch (NullPointerException e) {
743
            String msg = "The 'fragment' parameter was not provided, using default";
744
            logMetacat.warn(msg);
745
        }
746 6604 cjones
747 7441 leinfelder
        // call the service
748
        Identifier identifier = MNodeService.getInstance(request).generateIdentifier(session, scheme, fragment);
749
        response.setStatus(200);
750
        response.setContentType("text/xml");
751
        OutputStream out = response.getOutputStream();
752
        TypeMarshaller.marshalTypeToOutputStream(identifier, out);
753
    }
754
755 6604 cjones
    /**
756 6273 leinfelder
     * Checks the access policy
757
     * @param id
758
     * @return
759
     * @throws ServiceFailure
760
     * @throws InvalidToken
761
     * @throws NotFound
762
     * @throws NotAuthorized
763
     * @throws NotImplemented
764
     * @throws InvalidRequest
765
     */
766 6264 leinfelder
    private boolean isAuthorized(String id) throws ServiceFailure, InvalidToken, NotFound, NotAuthorized, NotImplemented, InvalidRequest {
767
		Identifier pid = new Identifier();
768
		pid.setValue(id);
769
		Permission permission = null;
770
		try {
771 6509 leinfelder
			String perm = params.get("action")[0];
772 6373 leinfelder
			permission = Permission.convert(perm);
773 6264 leinfelder
		} catch (Exception e) {
774
			logMetacat.warn("No permission specified");
775
		}
776 6542 leinfelder
		boolean result = MNodeService.getInstance(request).isAuthorized(session, pid, permission);
777 6264 leinfelder
		response.setStatus(200);
778
		response.setContentType("text/xml");
779
		return result;
780
    }
781
782 8874 leinfelder
    private void getToken() throws Exception {
783
784
		if (this.session != null) {
785
			String userId = this.session.getSubject().getValue();
786 9009 leinfelder
			String fullName = null;
787
			try {
788
				Person person = this.session.getSubjectInfo().getPerson(0);
789
				fullName = person.getGivenName(0) + " " + person.getFamilyName();
790
			} catch (Exception e) {
791
				logMetacat.warn(e.getMessage(), e);
792
			}
793
794 8874 leinfelder
			String token = null;
795 9164 leinfelder
			token = TokenGenerator.getInstance().getJWT(userId, fullName);
796 8874 leinfelder
797
			response.setStatus(200);
798
			response.setContentType("text/plain");
799
	        OutputStream out = response.getOutputStream();
800
	        out.write(token.getBytes(MetaCatServlet.DEFAULT_ENCODING));
801
	        out.close();
802
		} else {
803
			response.setStatus(401);
804
			response.setContentType("text/plain");
805
			OutputStream out = response.getOutputStream();
806
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
807
	        out.close();
808
		}
809
810
    }
811
812 9173 leinfelder
    private void whoami() throws Exception {
813
814
		if (this.session != null) {
815
			Subject subject = this.session.getSubject();
816
			SubjectInfo subjectInfo = null;
817
			try {
818
				subjectInfo = this.session.getSubjectInfo();
819
			} catch (Exception e) {
820
				logMetacat.warn(e.getMessage(), e);
821
			}
822
823
			response.setStatus(200);
824
			response.setContentType("text/plain");
825
	        OutputStream out = response.getOutputStream();
826
827
	        if (subjectInfo != null) {
828
		        TypeMarshaller.marshalTypeToOutputStream(subjectInfo, out);
829
	        } else {
830
		        TypeMarshaller.marshalTypeToOutputStream(subject, out);
831
	        }
832
833
	        out.close();
834
		} else {
835
			response.setStatus(401);
836
			response.setContentType("text/plain");
837
			OutputStream out = response.getOutputStream();
838
	        out.write("No session information found".getBytes(MetaCatServlet.DEFAULT_ENCODING));
839
	        out.close();
840
		}
841
842
    }
843
844 6273 leinfelder
    /**
845
     * Processes failed synchronization message
846
     * @throws NotImplemented
847
     * @throws ServiceFailure
848
     * @throws NotAuthorized
849
     * @throws InvalidRequest
850
     * @throws JiBXException
851 6367 leinfelder
     * @throws IllegalAccessException
852
     * @throws InstantiationException
853
     * @throws IOException
854 6273 leinfelder
     */
855 6367 leinfelder
    private void syncError() throws NotImplemented, ServiceFailure, NotAuthorized, InvalidRequest, JiBXException, IOException, InstantiationException, IllegalAccessException {
856 6264 leinfelder
    	SynchronizationFailed syncFailed = null;
857 6472 leinfelder
		try {
858
			syncFailed = collectSynchronizationFailed();
859
		} catch (ParserConfigurationException e) {
860
			throw new ServiceFailure("2161", e.getMessage());
861
		} catch (SAXException e) {
862
			throw new ServiceFailure("2161", e.getMessage());
863
		}
864
865 6542 leinfelder
		MNodeService.getInstance(request).synchronizationFailed(session, syncFailed);
866 6264 leinfelder
    }
867 6472 leinfelder
868 8810 leinfelder
	/**
869 6273 leinfelder
     * Calculate the checksum
870
     * @throws NotImplemented
871
     * @throws JiBXException
872
     * @throws IOException
873
     * @throws InvalidToken
874
     * @throws ServiceFailure
875
     * @throws NotAuthorized
876
     * @throws NotFound
877
     * @throws InvalidRequest
878
     */
879 6285 cjones
    private void checksum(String pid) throws NotImplemented, JiBXException, IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest {
880 6264 leinfelder
    	String checksumAlgorithm = "MD5";
881 7222 leinfelder
    	try {
882
    		checksumAlgorithm = PropertyService.getProperty("dataone.checksumAlgorithm.default");
883
        } catch(Exception e) {
884
        	logMetacat.warn("Could not lookup configured default checksum algorithm, using: " + checksumAlgorithm);
885
        }
886 6284 leinfelder
887 6285 cjones
        Identifier pidid = new Identifier();
888
        pidid.setValue(pid);
889 6264 leinfelder
        try {
890
            checksumAlgorithm = params.get("checksumAlgorithm")[0];
891
        } catch(Exception e) {
892 7222 leinfelder
            //do nothing.  use the default
893 6264 leinfelder
        	logMetacat.warn("No algorithm specified, using default: " + checksumAlgorithm);
894
        }
895 6285 cjones
        logMetacat.debug("getting checksum for object " + pid + " with algorithm " + checksumAlgorithm);
896 6273 leinfelder
897 6542 leinfelder
        Checksum c = MNodeService.getInstance(request).getChecksum(session, pidid, checksumAlgorithm);
898 6273 leinfelder
        logMetacat.debug("got checksum " + c.getValue());
899
        response.setStatus(200);
900
        logMetacat.debug("serializing response");
901 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(c, response.getOutputStream());
902 6273 leinfelder
        logMetacat.debug("done serializing response.");
903
904 6264 leinfelder
    }
905
906
	/**
907
     * handle the replicate action for MN
908 6273 leinfelder
	 * @throws JiBXException
909
	 * @throws FileUploadException
910
	 * @throws IOException
911
	 * @throws InvalidRequest
912
	 * @throws ServiceFailure
913
	 * @throws UnsupportedType
914
	 * @throws InsufficientResources
915
	 * @throws NotAuthorized
916
	 * @throws NotImplemented
917 6367 leinfelder
	 * @throws IllegalAccessException
918
	 * @throws InstantiationException
919 7142 leinfelder
	 * @throws InvalidToken
920 6264 leinfelder
     */
921 6678 cjones
    private void replicate()
922
        throws ServiceFailure, InvalidRequest, IOException, FileUploadException,
923
        JiBXException, NotImplemented, NotAuthorized, InsufficientResources,
924 7142 leinfelder
        UnsupportedType, InstantiationException, IllegalAccessException, InvalidToken {
925 6264 leinfelder
926 6272 leinfelder
        logMetacat.debug("in POST replicate()");
927 6264 leinfelder
928 7064 leinfelder
        // somewhat unorthodox, but the call is asynchronous and we'd like to return this info sooner
929 7142 leinfelder
        boolean allowed = false;
930 7064 leinfelder
        if (session == null) {
931
        	String msg = "No session was provided.";
932
            NotAuthorized failure = new NotAuthorized("2152", msg);
933
        	throw failure;
934 7142 leinfelder
        } else {
935
        	allowed = MNodeService.getInstance(request).isAdminAuthorized(session);
936
        	if (!allowed) {
937
        		String msg = "User is not an admin user";
938
                NotAuthorized failure = new NotAuthorized("2152", msg);
939
            	throw failure;
940
        	}
941 7064 leinfelder
        }
942
943 8810 leinfelder
        // parse the systemMetadata
944
        Map<String, File> files = collectMultipartFiles();
945
        final SystemMetadata sysmeta = TypeMarshaller.unmarshalTypeFromFile(SystemMetadata.class, files.get("sysmeta"));
946 6264 leinfelder
947 6273 leinfelder
        String sn = multipartparams.get("sourceNode").get(0);
948 6272 leinfelder
        logMetacat.debug("sourceNode: " + sn);
949 6679 leinfelder
        final NodeReference sourceNode = new NodeReference();
950 6548 cjones
        sourceNode.setValue(sn);
951 6679 leinfelder
952
        // run it in a thread to avoid connection timeout
953
        Runnable runner = new Runnable() {
954
			@Override
955
			public void run() {
956
				try {
957
			        MNodeService.getInstance(request).replicate(session, sysmeta, sourceNode);
958
				} catch (Exception e) {
959 7091 leinfelder
					logMetacat.error("Error running replication: " + e.getMessage(), e);
960 6679 leinfelder
					throw new RuntimeException(e.getMessage(), e);
961
				}
962
			}
963
    	};
964 7143 leinfelder
    	// submit the task, and that's it
965
    	executor.submit(runner);
966 6679 leinfelder
967
    	// thread was started, so we return success
968 6678 cjones
        response.setStatus(200);
969 6679 leinfelder
970 6264 leinfelder
    }
971 6604 cjones
972 6264 leinfelder
    /**
973 6604 cjones
     * Handle the getReplica action for the MN
974
     * @param id  the identifier for the object
975
     * @throws NotFound
976
     * @throws ServiceFailure
977
     * @throws NotImplemented
978
     * @throws NotAuthorized
979
     * @throws InvalidToken
980
     * @throws InvalidRequest
981
     */
982
    private void getReplica(String id)
983
        throws InvalidRequest, InvalidToken, NotAuthorized, NotImplemented,
984
        ServiceFailure, NotFound {
985
986
        Identifier pid = new Identifier();
987
        pid.setValue(id);
988
        OutputStream out = null;
989
        InputStream dataBytes = null;
990
991
        try {
992
            // call the service
993
            dataBytes = MNodeService.getInstance(request).getReplica(session, pid);
994
995
            response.setContentType("application/octet-stream");
996
            response.setStatus(200);
997
            out = response.getOutputStream();
998
            // write the object to the output stream
999
            IOUtils.copyLarge(dataBytes, out);
1000
1001
        } catch (IOException e) {
1002
            String msg = "There was an error writing the output: " + e.getMessage();
1003
            logMetacat.error(msg);
1004
            throw new ServiceFailure("2181", msg);
1005
1006
        }
1007
1008
    }
1009
1010
    /**
1011 6264 leinfelder
     * Get the Node information
1012
     *
1013
     * @throws JiBXException
1014
     * @throws IOException
1015
     * @throws InvalidRequest
1016
     * @throws ServiceFailure
1017
     * @throws NotAuthorized
1018
     * @throws NotImplemented
1019
     */
1020
    private void node()
1021
        throws JiBXException, IOException, NotImplemented, NotAuthorized, ServiceFailure, InvalidRequest {
1022
1023 6542 leinfelder
        Node n = MNodeService.getInstance(request).getCapabilities();
1024 6264 leinfelder
1025
        response.setContentType("text/xml");
1026
        response.setStatus(200);
1027 6501 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(n, response.getOutputStream());
1028 6264 leinfelder
1029
    }
1030
1031
    /**
1032
     * MN_crud.describe()
1033
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.describe
1034 6285 cjones
     * @param pid
1035 6273 leinfelder
     * @throws InvalidRequest
1036
     * @throws NotImplemented
1037
     * @throws NotFound
1038
     * @throws NotAuthorized
1039
     * @throws ServiceFailure
1040
     * @throws InvalidToken
1041 6264 leinfelder
     */
1042 6285 cjones
    private void describeObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest
1043 6264 leinfelder
    {
1044 7043 leinfelder
1045 6264 leinfelder
        response.setContentType("text/xml");
1046 7043 leinfelder
1047 6264 leinfelder
        Identifier id = new Identifier();
1048 6285 cjones
        id.setValue(pid);
1049 6273 leinfelder
1050 7043 leinfelder
        DescribeResponse dr = null;
1051
        try {
1052
        	dr = MNodeService.getInstance(request).describe(session, id);
1053
        } catch (BaseException e) {
1054
        	response.setStatus(e.getCode());
1055
        	response.addHeader("DataONE-Exception-Name", e.getClass().getName());
1056
            response.addHeader("DataONE-Exception-DetailCode", e.getDetail_code());
1057
            response.addHeader("DataONE-Exception-Description", e.getDescription());
1058
            response.addHeader("DataONE-Exception-PID", id.getValue());
1059
            return;
1060
		}
1061
1062
        response.setStatus(200);
1063
1064 6502 leinfelder
        //response.addHeader("pid", pid);
1065 6507 leinfelder
        response.addHeader("DataONE-Checksum", dr.getDataONE_Checksum().getAlgorithm() + "," + dr.getDataONE_Checksum().getValue());
1066 6502 leinfelder
        response.addHeader("Content-Length", dr.getContent_Length() + "");
1067
        response.addHeader("Last-Modified", DateTimeMarshaller.serializeDateToUTC(dr.getLast_Modified()));
1068 6691 leinfelder
        response.addHeader("DataONE-ObjectFormat", dr.getDataONE_ObjectFormatIdentifier().getValue());
1069 6773 leinfelder
        response.addHeader("DataONE-SerialVersion", dr.getSerialVersion().toString());
1070
1071 6502 leinfelder
1072 6264 leinfelder
    }
1073
1074
    /**
1075
     * get the logs based on passed params.  Available
1076
     * See http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
1077
     * for more info
1078 6273 leinfelder
     * @throws NotImplemented
1079
     * @throws InvalidRequest
1080
     * @throws NotAuthorized
1081
     * @throws ServiceFailure
1082
     * @throws InvalidToken
1083
     * @throws IOException
1084
     * @throws JiBXException
1085 6264 leinfelder
     */
1086 6273 leinfelder
    private void getLog() throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, NotImplemented, IOException, JiBXException
1087 6264 leinfelder
    {
1088 6273 leinfelder
1089
        Date fromDate = null;
1090
        Date toDate = null;
1091 8810 leinfelder
        String event = null;
1092 6273 leinfelder
        Integer start = null;
1093
        Integer count = null;
1094 7099 leinfelder
        String pidFilter = null;
1095 6273 leinfelder
1096 6264 leinfelder
        try {
1097 6273 leinfelder
        	String fromDateS = params.get("fromDate")[0];
1098
            logMetacat.debug("param fromDateS: " + fromDateS);
1099 6469 leinfelder
            fromDate = DateTimeMarshaller.deserializeDateToUTC(fromDateS);
1100 6273 leinfelder
        } catch (Exception e) {
1101
        	logMetacat.warn("Could not parse fromDate: " + e.getMessage());
1102 6264 leinfelder
        }
1103 6273 leinfelder
        try {
1104
        	String toDateS = params.get("toDate")[0];
1105
            logMetacat.debug("param toDateS: " + toDateS);
1106 6469 leinfelder
            toDate = DateTimeMarshaller.deserializeDateToUTC(toDateS);
1107 6273 leinfelder
        } catch (Exception e) {
1108
        	logMetacat.warn("Could not parse toDate: " + e.getMessage());
1109
		}
1110
        try {
1111 8810 leinfelder
        	event = params.get("event")[0];
1112 6273 leinfelder
        } catch (Exception e) {
1113
        	logMetacat.warn("Could not parse event: " + e.getMessage());
1114
		}
1115
        logMetacat.debug("fromDate: " + fromDate + " toDate: " + toDate);
1116
1117
        try {
1118
        	start =  Integer.parseInt(params.get("start")[0]);
1119
        } catch (Exception e) {
1120
			logMetacat.warn("Could not parse start: " + e.getMessage());
1121
		}
1122
        try {
1123
        	count =  Integer.parseInt(params.get("count")[0]);
1124
        } catch (Exception e) {
1125
			logMetacat.warn("Could not parse count: " + e.getMessage());
1126
		}
1127
1128 7099 leinfelder
        try {
1129
            pidFilter = params.get("pidFilter")[0];
1130
        } catch (Exception e) {
1131
            logMetacat.warn("Could not parse pidFilter: " + e.getMessage());
1132
        }
1133
1134 6273 leinfelder
        logMetacat.debug("calling getLogRecords");
1135 7101 leinfelder
        Log log = MNodeService.getInstance(request).getLogRecords(session, fromDate, toDate, event, pidFilter, start, count);
1136 6273 leinfelder
1137
        OutputStream out = response.getOutputStream();
1138
        response.setStatus(200);
1139
        response.setContentType("text/xml");
1140
1141 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(log, out);
1142 6273 leinfelder
1143 6264 leinfelder
    }
1144
1145 7417 leinfelder
1146
1147 6264 leinfelder
    /**
1148
     * Implements REST version of DataONE CRUD API --> get
1149 6285 cjones
     * @param pid ID of data object to be read
1150 6273 leinfelder
     * @throws NotImplemented
1151
     * @throws InvalidRequest
1152
     * @throws NotFound
1153
     * @throws NotAuthorized
1154
     * @throws ServiceFailure
1155
     * @throws InvalidToken
1156
     * @throws IOException
1157
     * @throws JiBXException
1158 6264 leinfelder
     */
1159 6285 cjones
    protected void getObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1160 6264 leinfelder
        OutputStream out = null;
1161 6273 leinfelder
1162 6285 cjones
        if (pid != null) { //get a specific document
1163 6273 leinfelder
            Identifier id = new Identifier();
1164 6285 cjones
            id.setValue(pid);
1165 6273 leinfelder
1166 6542 leinfelder
            SystemMetadata sm = MNodeService.getInstance(request).getSystemMetadata(session, id);
1167 6264 leinfelder
1168 7412 leinfelder
            // set the headers for the content
1169 9235 leinfelder
            String mimeType = null;
1170
            String charset = null;
1171
1172
            // do we have mediaType/encoding in SM?
1173
            MediaType mediaType = sm.getMediaType();
1174
            if (mediaType == null) {
1175
            	try {
1176
            		mediaType = ObjectFormatCache.getInstance().getFormat(sm.getFormatId()).getMediaType();
1177
            	} catch (BaseException be) {
1178
            		logMetacat.warn("Could not lookup ObjectFormat MediaType for: " + sm.getFormatId(), be);
1179
            	}
1180
            }
1181
            if (mediaType != null) {
1182
                mimeType = sm.getMediaType().getName();
1183
                if (sm.getMediaType().getPropertyList() != null) {
1184
                	Iterator<MediaTypeProperty> iter = sm.getMediaType().getPropertyList().iterator();
1185
                	while (iter.hasNext()) {
1186
                		MediaTypeProperty mtp = iter.next();
1187
                		if (mtp.getName().equalsIgnoreCase("charset")) {
1188
                			charset = mtp.getValue();
1189
                			mimeType += "; charset=" + charset;
1190
                			break;
1191
                		}
1192
                	}
1193
                }
1194
            }
1195
            // check object format
1196
1197
            // use the fallback from v1 impl
1198 7412 leinfelder
            if (mimeType == null) {
1199 9235 leinfelder
	            mimeType = ObjectFormatInfo.instance().getMimeType(sm.getFormatId().getValue());
1200
1201
	            // still null?
1202
	            if (mimeType == null) {
1203
	            	mimeType = "application/octet-stream";
1204
	            }
1205 6273 leinfelder
            }
1206 9235 leinfelder
1207
            // check for filename in SM first
1208
            String filename = sm.getFileName();
1209
            // then fallback to using id and extension
1210
            if (filename == null) {
1211
	            String extension = ObjectFormatInfo.instance().getExtension(sm.getFormatId().getValue());
1212
	            filename = id.getValue();
1213
	            if (extension != null) {
1214
	            	filename = id.getValue() + extension;
1215
	            }
1216 7412 leinfelder
            }
1217
            response.setContentType(mimeType);
1218
            response.setHeader("Content-Disposition", "inline; filename=" + filename);
1219 6273 leinfelder
1220 6542 leinfelder
            InputStream data = MNodeService.getInstance(request).get(session, id);
1221 6273 leinfelder
1222
            out = response.getOutputStream();
1223
            IOUtils.copyLarge(data, out);
1224
1225
        }
1226
        else
1227
        { //call listObjects with specified params
1228
            Date startTime = null;
1229
            Date endTime = null;
1230 7100 leinfelder
            ObjectFormatIdentifier formatId = null;
1231 8810 leinfelder
            Identifier identifier = null;
1232 7462 leinfelder
            boolean replicaStatus = true;
1233 6273 leinfelder
            int start = 0;
1234
            //TODO: make the max count into a const
1235
            int count = 1000;
1236
            Enumeration paramlist = request.getParameterNames();
1237
            while (paramlist.hasMoreElements())
1238
            { //parse the params and make the crud call
1239
                String name = (String) paramlist.nextElement();
1240
                String[] value = (String[])request.getParameterValues(name);
1241
1242 6799 cjones
                if (name.equals("fromDate") && value != null)
1243 6264 leinfelder
                {
1244 6273 leinfelder
                    try
1245 6264 leinfelder
                    {
1246 6273 leinfelder
                      //startTime = dateFormat.parse(value[0]);
1247 6469 leinfelder
                    	startTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1248
                        //startTime = parseDateAndConvertToGMT(value[0]);
1249 6264 leinfelder
                    }
1250 6273 leinfelder
                    catch(Exception e)
1251 6799 cjones
                    {  //if we can't parse it, just don't use the fromDate param
1252
                        logMetacat.warn("Could not parse fromDate: " + value[0]);
1253 6273 leinfelder
                        startTime = null;
1254 6264 leinfelder
                    }
1255 6273 leinfelder
                }
1256 6799 cjones
                else if(name.equals("toDate") && value != null)
1257 6273 leinfelder
                {
1258
                    try
1259 6264 leinfelder
                    {
1260 6469 leinfelder
                    	endTime = DateTimeMarshaller.deserializeDateToUTC(value[0]);
1261 6264 leinfelder
                    }
1262 6273 leinfelder
                    catch(Exception e)
1263 6799 cjones
                    {  //if we can't parse it, just don't use the toDate param
1264
                        logMetacat.warn("Could not parse toDate: " + value[0]);
1265 6273 leinfelder
                        endTime = null;
1266
                    }
1267 6264 leinfelder
                }
1268 7100 leinfelder
                else if(name.equals("formatId") && value != null)
1269 6264 leinfelder
                {
1270 7100 leinfelder
                	formatId = new ObjectFormatIdentifier();
1271
                	formatId.setValue(value[0]);
1272 6264 leinfelder
                }
1273 8810 leinfelder
                else if(name.equals("identifier") && value != null)
1274
                {
1275
                	identifier = new Identifier();
1276
                	identifier.setValue(value[0]);
1277
                }
1278 6273 leinfelder
                else if(name.equals("replicaStatus") && value != null)
1279 6264 leinfelder
                {
1280 6273 leinfelder
                    if(value != null &&
1281
                       value.length > 0 &&
1282 7462 leinfelder
                       (value[0].equalsIgnoreCase("false") || value[0].equalsIgnoreCase("no")))
1283 6273 leinfelder
                    {
1284 7462 leinfelder
                        replicaStatus = false;
1285 6273 leinfelder
                    }
1286 6264 leinfelder
                }
1287 6273 leinfelder
                else if(name.equals("start") && value != null)
1288 6264 leinfelder
                {
1289 6273 leinfelder
                    start = new Integer(value[0]).intValue();
1290 6264 leinfelder
                }
1291 6273 leinfelder
                else if(name.equals("count") && value != null)
1292 6264 leinfelder
                {
1293 6273 leinfelder
                    count = new Integer(value[0]).intValue();
1294 6264 leinfelder
                }
1295
            }
1296 6273 leinfelder
            //make the crud call
1297
            logMetacat.debug("session: " + session + " startTime: " + startTime +
1298 7100 leinfelder
                    " endTime: " + endTime + " formatId: " +
1299
                    formatId + " replicaStatus: " + replicaStatus +
1300 6273 leinfelder
                    " start: " + start + " count: " + count);
1301
1302
            ObjectList ol =
1303 6542 leinfelder
           	 MNodeService.getInstance(request).listObjects(session, startTime, endTime,
1304 8810 leinfelder
           			formatId, identifier, replicaStatus, start, count);
1305 6352 cjones
1306 6273 leinfelder
            out = response.getOutputStream();
1307
            response.setStatus(200);
1308
            response.setContentType("text/xml");
1309
            // Serialize and write it to the output stream
1310 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(ol, out);
1311 6273 leinfelder
1312 6264 leinfelder
        }
1313 6273 leinfelder
1314 6264 leinfelder
    }
1315
1316 6273 leinfelder
1317 6264 leinfelder
    /**
1318 7855 leinfelder
     * Retrieve data package as Bagit zip
1319
     * @param pid
1320
     * @throws NotImplemented
1321
     * @throws NotFound
1322
     * @throws NotAuthorized
1323
     * @throws ServiceFailure
1324
     * @throws InvalidToken
1325
     * @throws IOException
1326 8810 leinfelder
     * @throws InvalidRequest
1327 7855 leinfelder
     */
1328 8810 leinfelder
    protected void getPackage(String format, String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, InvalidRequest {
1329 7855 leinfelder
1330
        Identifier id = new Identifier();
1331
        id.setValue(pid);
1332 8810 leinfelder
        ObjectFormatIdentifier formatId = null;
1333
        if (format != null) {
1334
        	formatId = new ObjectFormatIdentifier();
1335
        	formatId.setValue(format);
1336
        }
1337
		InputStream is = MNodeService.getInstance(request).getPackage(session, formatId , id);
1338 7855 leinfelder
1339 8160 leinfelder
        // use the provided filename
1340
        String filename = null;
1341
        if (is instanceof DeleteOnCloseFileInputStream) {
1342
            filename = ((DeleteOnCloseFileInputStream)is).getFile().getName();
1343
        } else {
1344
        	filename = "dataPackage-" + System.currentTimeMillis() + ".zip";
1345
        }
1346 8034 leinfelder
        response.setHeader("Content-Disposition", "inline; filename=" + filename);
1347 7855 leinfelder
        response.setContentType("application/zip");
1348
        response.setStatus(200);
1349
        OutputStream out = response.getOutputStream();
1350
1351
        // write it to the output stream
1352
        IOUtils.copyLarge(is, out);
1353
   }
1354
1355 7864 leinfelder
	protected void publish(String pid) throws InvalidToken, ServiceFailure,
1356
			NotAuthorized, NotFound, NotImplemented, IOException,
1357
			JiBXException, InvalidRequest, IdentifierNotUnique,
1358
			UnsupportedType, InsufficientResources, InvalidSystemMetadata {
1359
1360
		// publish the object
1361
		Identifier originalIdentifier = new Identifier();
1362
		originalIdentifier.setValue(pid);
1363
		Identifier newIdentifier = MNodeService.getInstance(request).publish(session, originalIdentifier);
1364
1365
		response.setStatus(200);
1366
		response.setContentType("text/xml");
1367
		OutputStream out = response.getOutputStream();
1368
1369
		// write new identifier to the output stream
1370
		TypeMarshaller.marshalTypeToOutputStream(newIdentifier, out);
1371
	}
1372
1373 7855 leinfelder
    /**
1374 6273 leinfelder
     * Retrieve System Metadata
1375 6285 cjones
     * @param pid
1376 6273 leinfelder
     * @throws InvalidToken
1377
     * @throws ServiceFailure
1378
     * @throws NotAuthorized
1379
     * @throws NotFound
1380
     * @throws InvalidRequest
1381
     * @throws NotImplemented
1382
     * @throws IOException
1383
     * @throws JiBXException
1384 6264 leinfelder
     */
1385 6285 cjones
    protected void getSystemMetadataObject(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, InvalidRequest, NotImplemented, IOException, JiBXException {
1386 6273 leinfelder
1387
        Identifier id = new Identifier();
1388 6285 cjones
        id.setValue(pid);
1389 6542 leinfelder
        SystemMetadata sysmeta = MNodeService.getInstance(request).getSystemMetadata(session, id);
1390 6273 leinfelder
1391
        response.setContentType("text/xml");
1392
        response.setStatus(200);
1393
        OutputStream out = response.getOutputStream();
1394
1395
        // Serialize and write it to the output stream
1396 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(sysmeta, out);
1397 6273 leinfelder
   }
1398 6264 leinfelder
1399
1400
    /**
1401
     * Inserts or updates the object
1402
     *
1403 6285 cjones
     * @param pid - ID of data object to be inserted or updated.  If action is update, the pid
1404 6264 leinfelder
     *               is the existing pid.  If insert, the pid is the new one
1405 6269 leinfelder
     * @throws InvalidRequest
1406
     * @throws ServiceFailure
1407
     * @throws JiBXException
1408
     * @throws NotImplemented
1409
     * @throws InvalidSystemMetadata
1410
     * @throws InsufficientResources
1411
     * @throws UnsupportedType
1412
     * @throws IdentifierNotUnique
1413
     * @throws NotAuthorized
1414
     * @throws InvalidToken
1415
     * @throws NotFound
1416 6273 leinfelder
     * @throws IOException
1417 6367 leinfelder
     * @throws IllegalAccessException
1418
     * @throws InstantiationException
1419 6264 leinfelder
     */
1420 6972 leinfelder
    protected void putObject(String trailingPid, String action) throws ServiceFailure, InvalidRequest, JiBXException, InvalidToken, NotAuthorized, IdentifierNotUnique, UnsupportedType, InsufficientResources, InvalidSystemMetadata, NotImplemented, NotFound, IOException, InstantiationException, IllegalAccessException {
1421
1422
    	// Read the incoming data from its Mime Multipart encoding
1423 6269 leinfelder
    	Map<String, File> files = collectMultipartFiles();
1424 6972 leinfelder
1425 6975 leinfelder
    	Identifier pid = new Identifier();
1426 6972 leinfelder
        if (trailingPid == null) {
1427 6975 leinfelder
	        // get the pid string from the body and set the value
1428
	        String pidString = multipartparams.get("pid").get(0);
1429 7051 cjones
	        if (pidString != null) {
1430
            pid.setValue(pidString);
1431
1432
          } else {
1433
              throw new InvalidRequest("1102", "The pid param must be included and contain the identifier.");
1434
1435
          }
1436 6972 leinfelder
        } else {
1437
        	// use the pid included in the URL
1438
        	pid.setValue(trailingPid);
1439
        }
1440
        logMetacat.debug("putObject with pid " + pid.getValue());
1441
        logMetacat.debug("Entering putObject: " + pid.getValue() + "/" + action);
1442
1443 6269 leinfelder
        InputStream object = null;
1444
        InputStream sysmeta = null;
1445
        File smFile = files.get("sysmeta");
1446
        sysmeta = new FileInputStream(smFile);
1447
        File objFile = files.get("object");
1448
        object = new FileInputStream(objFile);
1449
1450 6927 cjones
        // ensure we have the object bytes
1451
        if  ( objFile == null ) {
1452
            throw new InvalidRequest("1102", "The object param must contain the object bytes.");
1453
1454
        }
1455
1456
        // ensure we have the system metadata
1457
        if  ( smFile == null ) {
1458
            throw new InvalidRequest("1102", "The sysmeta param must contain the system metadata document.");
1459
1460
        }
1461
1462 6972 leinfelder
        response.setStatus(200);
1463
        response.setContentType("text/xml");
1464
        OutputStream out = response.getOutputStream();
1465
1466 6269 leinfelder
        if (action.equals(FUNCTION_NAME_INSERT)) {
1467
            // handle inserts
1468
            logMetacat.debug("Commence creation...");
1469 6367 leinfelder
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1470 6264 leinfelder
1471 6972 leinfelder
            logMetacat.debug("creating object with pid " + pid.getValue());
1472
            Identifier rId = MNodeService.getInstance(request).create(session, pid, object, smd);
1473 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1474 6264 leinfelder
1475 6269 leinfelder
        } else if (action.equals(FUNCTION_NAME_UPDATE)) {
1476
        	// handle updates
1477
1478
            // construct pids
1479 6972 leinfelder
            Identifier newPid = null;
1480 6269 leinfelder
            try {
1481
            	String newPidString = multipartparams.get("newPid").get(0);
1482 6975 leinfelder
            	newPid = new Identifier();
1483
            	newPid.setValue(newPidString);
1484 6269 leinfelder
            } catch (Exception e) {
1485 6972 leinfelder
				logMetacat.error("Could not get newPid from request");
1486 6269 leinfelder
			}
1487
            logMetacat.debug("Commence update...");
1488
1489
            // get the systemmetadata object
1490 6367 leinfelder
            SystemMetadata smd = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1491 6264 leinfelder
1492 6972 leinfelder
            Identifier rId = MNodeService.getInstance(request).update(session, pid, object, newPid, smd);
1493 6367 leinfelder
            TypeMarshaller.marshalTypeToOutputStream(rId, out);
1494 6269 leinfelder
        } else {
1495
            throw new InvalidRequest("1000", "Operation must be create or update.");
1496
        }
1497 6972 leinfelder
1498 6264 leinfelder
    }
1499
1500
    /**
1501
     * Handle delete
1502 6285 cjones
     * @param pid ID of data object to be deleted
1503 6264 leinfelder
     * @throws IOException
1504 6273 leinfelder
     * @throws InvalidRequest
1505
     * @throws NotImplemented
1506
     * @throws NotFound
1507
     * @throws NotAuthorized
1508
     * @throws ServiceFailure
1509
     * @throws InvalidToken
1510
     * @throws JiBXException
1511 6264 leinfelder
     */
1512 6285 cjones
    private void deleteObject(String pid) throws IOException, InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, InvalidRequest, JiBXException
1513 6264 leinfelder
    {
1514
1515
        OutputStream out = response.getOutputStream();
1516
        response.setStatus(200);
1517
        response.setContentType("text/xml");
1518
1519
        Identifier id = new Identifier();
1520 6285 cjones
        id.setValue(pid);
1521 6273 leinfelder
1522
        logMetacat.debug("Calling delete");
1523 6542 leinfelder
        MNodeService.getInstance(request).delete(session, id);
1524 6367 leinfelder
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1525 6273 leinfelder
1526 7153 leinfelder
    }
1527
1528
    /**
1529
     * Archives the given pid
1530
     * @param pid
1531
     * @throws InvalidToken
1532
     * @throws ServiceFailure
1533
     * @throws NotAuthorized
1534
     * @throws NotFound
1535
     * @throws NotImplemented
1536
     * @throws IOException
1537
     * @throws JiBXException
1538
     */
1539
    private void archive(String pid) throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, NotImplemented, IOException, JiBXException {
1540 6264 leinfelder
1541 7153 leinfelder
        OutputStream out = response.getOutputStream();
1542
        response.setStatus(200);
1543
        response.setContentType("text/xml");
1544
1545
        Identifier id = new Identifier();
1546
        id.setValue(pid);
1547
1548 7154 leinfelder
        logMetacat.debug("Calling archive");
1549 7153 leinfelder
        MNodeService.getInstance(request).archive(session, id);
1550
1551
        TypeMarshaller.marshalTypeToOutputStream(id, out);
1552
1553
    }
1554
1555 6514 leinfelder
	protected SynchronizationFailed collectSynchronizationFailed() throws IOException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException, ParserConfigurationException, SAXException  {
1556
1557
		// Read the incoming data from its Mime Multipart encoding
1558
		logMetacat.debug("Disassembling MIME multipart form");
1559
		InputStream sf = null;
1560
1561
		// handle MMP inputs
1562
		File tmpDir = getTempDirectory();
1563
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
1564
		MultipartRequestResolver mrr =
1565 9099 jones
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), MAX_UPLOAD_SIZE, 0);
1566 6514 leinfelder
		MultipartRequest mr = null;
1567
		try {
1568
			mr = mrr.resolveMultipart(request);
1569
		} catch (Exception e) {
1570
			throw new ServiceFailure("2161",
1571
					"Could not resolve multipart: " + e.getMessage());
1572
		}
1573
		logMetacat.debug("resolved multipart request");
1574
		Map<String, File> files = mr.getMultipartFiles();
1575
		if (files == null || files.keySet() == null) {
1576
			throw new InvalidRequest("2163",
1577
					"must have multipart file with name 'message'");
1578
		}
1579
		logMetacat.debug("got multipart files");
1580
1581
		multipartparams = mr.getMultipartParameters();
1582
1583
		File sfFile = files.get("message");
1584
		if (sfFile == null) {
1585
			throw new InvalidRequest("2163",
1586
					"Missing the required file-part 'message' from the multipart request.");
1587
		}
1588
		logMetacat.debug("sfFile: " + sfFile.getAbsolutePath());
1589
		sf = new FileInputStream(sfFile);
1590
1591
		SynchronizationFailed syncFailed = (SynchronizationFailed) ExceptionHandler.deserializeXml(sf, "Error deserializing exception");
1592
		return syncFailed;
1593
	}
1594 9122 tao
1595
	/**
1596
	 * Update the system metadata for a specified identifier
1597
	 * @throws ServiceFailure
1598
	 * @throws InvalidRequest
1599
	 * @throws InstantiationException
1600
	 * @throws IllegalAccessException
1601
	 * @throws IOException
1602
	 * @throws JiBXException
1603
	 * @throws NotImplemented
1604
	 * @throws NotAuthorized
1605
	 * @throws InvalidSystemMetadata
1606
	 * @throws InvalidToken
1607
	 */
1608
	protected void updateSystemMetadata() throws ServiceFailure, InvalidRequest,
1609
	                        InstantiationException, IllegalAccessException, IOException, JiBXException, NotImplemented,
1610
	                        NotAuthorized, InvalidSystemMetadata, InvalidToken {
1611
	    // Read the incoming data from its Mime Multipart encoding
1612
        Map<String, File> files = collectMultipartFiles();
1613
1614
        // get the encoded pid string from the body and make the object
1615
        String pidString = multipartparams.get("pid").get(0);
1616
        Identifier pid = new Identifier();
1617
        pid.setValue(pidString);
1618
1619
        logMetacat.debug("updateSystemMetadata: " + pid);
1620 6514 leinfelder
1621 9122 tao
        // get the system metadata from the request
1622
        File smFile = files.get("sysmeta");
1623
        FileInputStream sysmeta = new FileInputStream(smFile);
1624
        SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
1625
1626
        logMetacat.debug("updating system metadata with pid " + pid.getValue());
1627
1628
        MNodeService.getInstance(request).updateSystemMetadata(session, pid, systemMetadata);
1629
	}
1630
1631 6264 leinfelder
}