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