Project

General

Profile

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

    
25
import java.io.*;
26
import java.util.*;
27

    
28
import javax.mail.BodyPart;
29
import javax.mail.MessagingException;
30
import javax.mail.internet.MimeMultipart;
31
import javax.servlet.ServletContext;
32
import javax.servlet.http.HttpServletRequest;
33
import javax.servlet.http.HttpServletResponse;
34
import java.text.DateFormat;
35
import java.text.ParseException;
36
import java.text.ParsePosition;
37
import java.text.SimpleDateFormat;
38

    
39

    
40
import org.apache.commons.httpclient.util.DateParser;
41
import org.apache.commons.io.IOUtils;
42
import org.apache.log4j.Logger;
43
import org.apache.maven.artifact.ant.shaded.IOUtil;
44
import org.dataone.service.exceptions.BaseException;
45
import org.dataone.service.exceptions.IdentifierNotUnique;
46
import org.dataone.service.exceptions.InsufficientResources;
47
import org.dataone.service.exceptions.InvalidRequest;
48
import org.dataone.service.exceptions.InvalidSystemMetadata;
49
import org.dataone.service.exceptions.InvalidToken;
50
import org.dataone.service.exceptions.NotAuthorized;
51
import org.dataone.service.exceptions.NotImplemented;
52
import org.dataone.service.exceptions.ServiceFailure;
53
import org.dataone.service.exceptions.UnsupportedType;
54
import org.dataone.service.exceptions.NotFound;
55
import org.dataone.service.types.*;
56
import org.jibx.runtime.BindingDirectory;
57
import org.jibx.runtime.IBindingFactory;
58
import org.jibx.runtime.IMarshallingContext;
59
import org.jibx.runtime.IUnmarshallingContext;
60
import org.jibx.runtime.JiBXException;
61

    
62
import edu.ucsb.nceas.metacat.DBUtil;
63
import edu.ucsb.nceas.metacat.IdentifierManager;
64
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
65
import edu.ucsb.nceas.metacat.MetacatHandler;
66
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
67
import edu.ucsb.nceas.metacat.dataone.CrudService;
68
import edu.ucsb.nceas.metacat.properties.PropertyService;
69
import edu.ucsb.nceas.metacat.service.SessionService;
70
import edu.ucsb.nceas.metacat.util.RequestUtil;
71
import edu.ucsb.nceas.metacat.util.SessionData;
72
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
73

    
74
import org.dataone.service.streaming.util.StreamUtil;
75

    
76
import com.gc.iotools.stream.is.InputStreamFromOutputStream;
77
/**
78
 * 
79
 * Implements Earthgrid REST API to Metacat <br/><br/> 
80
 * 
81
 * <ul>
82
 * <li>
83
 * <h3> EarthGrid Query Service</h3>
84
 * <ul><li>
85
 * <h3>Get & Authenticated Get:</h3> 
86
 * is equal to Metacat's read action and returns a data file having
87
 * the specified <doc-id> in the resource path. For authenticated Get service, a session id must be provided 
88
 * in the query string. <br/><br/>
89
 * 
90
 * <b>REST URL:</b>	<code>GET, [context-root]/object/[doc-id]?sessionid=[sessionid] </code><br/>
91
 * <b>Returns:</b> data file <br/><br/>
92
 * </li>
93
 * 
94
 * <li>
95
 * <h3>Query & Authenticated Query:</h3> 
96
 * Metacat's equivalent is squery action but this function 
97
 * receives a Earthgrid query document and returns Earthgrid resultset document by transforming those documents
98
 * to Metacat's equivalents by the means of Metacat Implementation in Earthgrid library. For authenticated Query service 
99
 * a session id must be provided in the query string. See Earthgrid (a.k.a. Ecogrid) project for XSD files of 
100
 * query and resultset documents<br/><br/>
101
 * 
102
 * <b>REST URL:</b>	<code>POST, [context-root]/object?sessionid=[sessionid]</code>    <br/>
103
 * <b>POST Data:</b> Earthgrid query document , Content-type: <code>text/xml</code><br/>
104
 * <b>Returns:</b> Earthgrid resultset document<br/><br/>
105
 * 
106
 * </li>
107
 * </ul>
108
 * 
109
 * </li>
110
 * 
111
 * <li>
112
 * <h3> EarthGrid Authentication Service</h3>
113
 * <ul><li>
114
 * <h3>Login: </h3> 
115
 * Receives username and password parameters in POST data and 
116
 * returns SessionId (in XML format) or failure message and uses MetacatHandler's handleLoginAction function<br/><br/>
117
 *  
118
 * <b>REST URL:</b> <code>POST, [context-root]/session?op=login</code> <br/>
119
 * <b>POST Data:</b> username=[username]&password=[password], Content-type: <code>application/x-www-form-urlencoded</code>
120
 * <b>Returns:</b> sessionId in XML format<br/><br/>
121
 * </li>
122
 * 
123
 * <li>
124
 * <h3>Logout: </h3> 
125
 * Receives session Id parameters in querystring and returns xml message, calls 
126
 * MetacatHandler's handleLogoutAction function<br/><br/>
127
 *  
128
 * <b>REST URL:</b>	<code>GET, [context-root]/session?op=logout&sessionid=[sessionid]</code>   <br/>
129
 * <b>Returns:</b> message in XML format<br/><br/>
130
 * </li>
131
 * </ul>
132
 * 
133
 * <li>
134
 * <h3>EarthGrid Put Service</h3>
135
 * 
136
 * <ul>
137
 * <li><h3>Update/Insert: </h3>		
138
 * <br/>
139
 * <b>REST URL:</b>	<code>PUT, [context-root]/object/[doc-id]?op={update|insert}&sessionid=[sessionid]</code>   <br/>
140
 * <b>POST Data:</b> document object, Content-type: <code>text/xml</code><br/>
141
 * <b>Returns:</b> message in XML format<br/><br/>
142
 * </li>
143
 * 
144
 * <li><h3>Delete: </h3>		
145
 * <br/>
146
 * <b>REST URL:</b>	<code>DELETE, [context-root]/object/[doc-id]?sessionid=[sessionid]</code>   <br/>
147
 * <b>Returns:</b> message in XML format<br/><br/>
148
 * </li>
149

    
150
 * </ul>
151
 * </li>
152
 * 
153
 * <li>
154
 * <h3>EarthGrid Identifier Service</h3><br/>
155
 * 
156
 * <ul>
157
 * <li><h3>isRegistered: </h3>		<br/>
158
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=isregistered</code>   <br/>
159
 * <b>Returns:</b> message in XML format<br/><br/>
160
 * </li>
161

    
162
 * <li><h3>getAllDocIds:</h3>		<br/>		
163
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getalldocids</code>   <br/>
164
 * <b>Returns:</b> document id list in XML format<br/><br/>
165
 * </li>
166
 * 
167
 * <li><h3>addLSID Function:</h3> 
168
 * Metacat does not support this function 		<br/>
169
 * <b>REST URL:</b>	<code>PUT, [context-root]/identifier/[doc-id]</code>   <br/>
170
 * <b>Returns:</b> error message in XML format<br/><br/>
171
 * </li>
172
 * 
173
 * <li><h3>getNextRevision:</h3>		<br/>
174
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier/[doc-id]?op=getnextrevision</code>   <br/>
175
 * <b>Returns:</b> message in XML format<br/><br/>
176
 * </li>
177
 * 
178
 * <li><h3>getNextObject:</h3>		<br/>
179
 * <b>REST URL:</b>	<code>GET, [context-root]/identifier?op=getnextobject&scope=[scope]</code>   <br/>
180
 * <b>Returns:</b> message in XML format<br/><br/>
181
 * </li>
182
 * 
183
 * </li>
184
 * </ul>
185
 * 
186
 */
187
public class ResourceHandler {
188

    
189
    /**HTTP Verb GET*/
190
    public static final byte GET = 1;
191
    /**HTTP Verb POST*/
192
    public static final byte POST = 2;
193
    /**HTTP Verb PUT*/
194
    public static final byte PUT = 3;
195
    /**HTTP Verb DELETE*/
196
    public static final byte DELETE = 4;
197

    
198
    /*
199
     * API Resources
200
     */
201
    private static final String RESOURCE_OBJECTS = "object";
202
    private static final String RESOURCE_META = "meta";
203
    private static final String RESOURCE_SESSION = "session";
204
    private static final String RESOURCE_IDENTIFIER = "identifier";
205
    private static final String RESOURCE_LOG = "log";
206

    
207
    /*
208
     * API Functions used as URL parameters
209
     */
210
    private static final String FUNCTION_KEYWORD = "op";
211
    private static final String FUNCTION_NAME_LOGIN = "login";
212
    private static final String FUNCTION_NAME_LOGOUT = "logout";
213
    private static final String FUNCTION_NAME_SET_ACCESS = "setaccess";
214
    private static final String FUNCTION_NAME_ISREGISTERED = "isregistered";
215
    private static final String FUNCTION_NAME_GETALLDOCS = "getalldocids";
216
    private static final String FUNCTION_NAME_GETNEXTREV = "getnextrevision";
217
    private static final String FUNCTION_NAME_GETNEXTOBJ = "getnextobject";
218
    private static final String FUNCTION_NAME_INSERT = "insert";
219
    private static final String FUNCTION_NAME_UPDATE = "update";
220
    private static final String FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA = "generatemissingsystemmetadata";
221

    
222
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
223
    
224
    private ServletContext servletContext;
225
    private Logger logMetacat;
226
    private MetacatHandler handler;
227
    private HttpServletRequest request;
228
    private HttpServletResponse response;
229
    private String username;
230
    private String password;
231
    private String sessionId;
232
    private String[] groupNames;
233

    
234
    private Hashtable<String, String[]> params;
235

    
236
    /**Initializes new instance by setting servlet context,request and response*/
237
    public ResourceHandler(ServletContext servletContext,
238
            HttpServletRequest request, HttpServletResponse response) {
239
        this.servletContext = servletContext;
240
        this.request = request;
241
        this.response = response;
242
    }
243

    
244
    /**
245
     * This function is called from REST APU servlet and handles each request to the servlet 
246
     * 
247
     * @param httpVerb (GET, POST, PUT or DELETE)
248
     */
249
    public void handle(byte httpVerb) {
250
        logMetacat = Logger.getLogger(ResourceHandler.class);
251
        try {
252
            String resource = request.getServletPath();
253
            String verb = "";
254
            
255
            System.out.println("handling verb " + httpVerb + " request with resource " + resource);
256
            boolean status = false;
257
            loadSessionData();
258

    
259
            if (resource != null) {
260
            	resource = request.getServletPath().substring(1);
261

    
262
            	params = new Hashtable<String, String[]>();
263
            	initParams();
264

    
265
            	Timer timer = new Timer();
266
            	handler = new MetacatHandler(timer);
267

    
268
            	if (resource.equals(RESOURCE_SESSION) && 
269
            			httpVerb == POST && 
270
            			params.get(FUNCTION_KEYWORD) != null) {
271
            		//System.out.println("function_keyword: " + params.get(FUNCTION_KEYWORD)[0]);
272
            		if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGIN)) {
273
            			login();
274
            			status = true;
275
            		} else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_LOGOUT)) {
276
            			logout();
277
            			status = true;
278
            		} else if (params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_SET_ACCESS)) {
279
            			setaccess();
280
            			status = true;
281
            			//System.out.println("done setting access");
282
            		}
283
            	} else if (resource.equals(RESOURCE_META)) {
284
            		if(params != null && params.get(FUNCTION_KEYWORD) != null &&
285
            				params.get(FUNCTION_KEYWORD)[0].equals(FUNCTION_NAME_GENERATE_MISSING_SYSTEM_METADATA))
286
            		{ //generate system metadata for any object that is
287
            			//a) not system metadata itself
288
            			//b) does not already have a system metadata id in the systemmetadata table
289
            			//c) not a BIN object (data)
290
            		    //TODO: check if we need this anymore.  Might be superceded
291
            		    //by MetacatPopulator
292
            			generateMissingSystemMetadata();
293
            			status = true;
294
            		}
295
            		else
296
            		{
297
            			String objectId = request.getPathInfo();
298
            			if (objectId != null && objectId.length() > 1) 
299
            			{
300
            				objectId = request.getPathInfo().substring(1);
301
            			}
302
            			getSystemMetadataObject(objectId);
303
            			status = true;
304
            		}
305

    
306
            	} else if (resource.equals(RESOURCE_OBJECTS)) {
307
            		logMetacat.debug("D1 Rest: Starting resource processing...");
308
            		loadSessionData();
309

    
310
            		String objectId = request.getPathInfo();
311
            		if (objectId != null && objectId.length() > 1) 
312
            		{
313
            			objectId = request.getPathInfo().substring(1);
314
            		}
315
            		else
316
            		{
317
            			objectId = null;
318
            		}
319

    
320
            		logMetacat.debug("verb:" + httpVerb);
321

    
322
            		if (httpVerb == GET) {
323
            			getObject(objectId);
324
            			status = true;
325
            		} else if (httpVerb == POST) {
326
            			putObject(objectId, FUNCTION_NAME_INSERT);
327
            			status = true;
328
            		} else if (httpVerb == PUT) {
329
            			putObject(objectId, FUNCTION_NAME_UPDATE);
330
            			status = true;
331
            		} else if (httpVerb == DELETE) {
332
            			deleteObject(objectId);
333
            			status = true;
334
            		}
335

    
336
            	} else if (resource.equals(RESOURCE_IDENTIFIER)) {
337

    
338
            		String identifierId = request.getPathInfo();
339
            		if (identifierId != null && identifierId.length() > 1)
340
            			identifierId = request.getPathInfo().substring(1); //trim the slash
341

    
342
            		if (httpVerb == GET) {
343
            			String op = params.get(FUNCTION_KEYWORD)[0];
344
            			if (op.equals(FUNCTION_NAME_ISREGISTERED)) {
345
            				isRegistered(identifierId);
346
            				status = true;
347
            			} else if (op.equals(FUNCTION_NAME_GETALLDOCS)) {
348
            				getAllDocIds();
349
            				status = true;
350
            			} else if (op.equals(FUNCTION_NAME_GETNEXTREV)) {
351
            				getNextRevision(identifierId);
352
            				status = true;
353
            			} else if (op.equals(FUNCTION_NAME_GETNEXTOBJ)) {
354
            				getNextObject();
355
            				status = true;
356
            			} 
357

    
358
            		} else if (httpVerb == PUT) {
359
            			//Earthgrid API > Identifier Service > addLSID Function 
360
            		    response.setStatus(501);
361
            			printError(
362
            					"This method is not supported by metacat.  To "
363
            					+ "add a new LSID, add a document to metacat.",
364
            					response);
365
            			status = true;
366
            		}
367

    
368
            	} else if (resource.equals(RESOURCE_LOG)) {
369
            		//handle log events
370
            		if(httpVerb == GET)
371
            		{
372
            			getLog();
373
            			status = true;
374
            		}
375
            		else
376
            		{
377
            		    //change to D1 spec for specifying which http methods are allowed for a resource
378
            		    response.setStatus(501);
379
            			printError("POST, PUT, DELETE is not supported for logs.", response);
380
            			status = true;
381
            		}
382

    
383
            	}
384

    
385
            	if (!status)
386
            	{
387
            	    response.setStatus(400);
388
            		printError("Incorrect parameters!", response);
389
            	}
390
            } else {
391
                response.setStatus(400);
392
            	printError("Incorrect resource!", response);
393
            }
394
        } catch (Exception e) {
395
        	logMetacat.error(e.getMessage());
396
        	e.printStackTrace();
397
        }
398
    }
399
    
400
    /**
401
     * get the logs from the CrudService based on passed params.  Available 
402
     * params are token, fromDate, toDate, event.  See 
403
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
404
     * for more info
405
     */
406
    private void getLog()
407
    {
408
        OutputStream out = null;
409
        try
410
        {
411
            out = response.getOutputStream();
412
            response.setStatus(200);
413
            response.setContentType("text/xml");
414
            AuthToken token = new AuthToken(sessionId);
415
            String fromDateS = params.get("fromDate")[0];
416
            System.out.println("param fromDateS: " + fromDateS);
417
            Date fromDate = null;
418
            String toDateS = params.get("toDate")[0];
419
            System.out.println("param toDateS: " + toDateS);
420
            Date toDate = null;
421
            String eventS = params.get("event")[0];
422
            Event event = null;
423
            if(fromDateS != null)
424
            {
425
                //fromDate = dateFormat.parse(fromDateS);
426
                fromDate = parseDateAndConvertToGMT(fromDateS);
427
            }
428
            if(toDateS != null)
429
            {
430
                //toDate = dateFormat.parse(toDateS);
431
                toDate = parseDateAndConvertToGMT(toDateS);
432
            }
433
            if(eventS != null)
434
            {
435
                event = Event.convert(eventS);
436
            }
437
            System.out.println("fromDate: " + fromDate + " toDate: " + toDate);
438
            
439
            System.out.println("calling crudservice.getLogRecords");
440
            Log log = CrudService.getInstance().getLogRecords(token, fromDate, toDate, event);
441
            serializeServiceType(Log.class, log, out);
442
        }
443
        catch(Exception e)
444
        {
445
            String msg = "Could not get logs from CrudService: " + e.getMessage();
446
            response.setStatus(500);
447
            ServiceFailure sf = new ServiceFailure("1490", msg);
448
            logMetacat.error(msg);
449
            e.printStackTrace();
450
            serializeException(sf, out);
451
        }
452
    }
453
    
454
    /**
455
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions  
456
     */
457
    private void initParams() {
458

    
459
        String name = null;
460
        String[] value = null;
461
        Enumeration paramlist = request.getParameterNames();
462
        while (paramlist.hasMoreElements()) {
463
            name = (String) paramlist.nextElement();
464
            value = request.getParameterValues(name);
465
            params.put(name, value);
466
        }
467
    }
468

    
469
    /**
470
     * 
471
     * Load user details of metacat session from the request 
472
     * 
473
     */
474
    private void loadSessionData()
475
      throws Exception
476
    {
477
        SessionData sessionData = RequestUtil.getSessionData(request);
478
        try
479
        {
480
            username = null;
481
            password = null;
482
            groupNames = null;
483
            sessionId = null;
484
            
485
            boolean validSession = false;
486
            SessionService ss = SessionService.getInstance();
487
            System.out.println("sessionData: " + sessionData);
488
            if(sessionData == null)
489
            {
490
                username = "public";
491
                sessionId = "0";
492
                System.out.println("sessiondata is null.  Creating a public session.");
493
                return;
494
            }
495
            
496
            System.out.println("username: " + sessionData.getUserName());
497
            System.out.println("sessionid: " + sessionData.getId());
498
            //validate the session
499
            if(ss.isSessionRegistered(sessionData.getId()) && 
500
               !(sessionData.getUserName().equals("public") || sessionData.getId().equals("0")))
501
            {
502
                validSession = true;
503
            }
504
            
505
            if(validSession)
506
            {
507
                //if the session is valid, set these variables
508
                username = sessionData.getUserName();
509
                password = sessionData.getPassword();
510
                groupNames = sessionData.getGroupNames();
511
                sessionId = sessionData.getId();
512
                System.out.println("setting sessionid to " + sessionId);
513
                System.out.println("username: " + username);
514
            }
515
            
516
            //if the session is not valid or the username is null, set
517
            //username to public
518
            if (username == null) 
519
            {
520
                System.out.println("setting username to public.");
521
                username = "public";
522
            }
523
        }
524
        catch(Exception e)
525
        {
526
            e.printStackTrace();
527
            throw new Exception("Could not load the session data: " + e.getMessage());
528
        }
529
    }
530
    
531
    /**
532
     * generate missing system metadata for any science metadata objects
533
     * that don't already have it. https://trac.dataone.org/ticket/591
534
     * 
535
     * called with POST meta/?op=generatemissingsystemmetadata
536
     */
537
    private void generateMissingSystemMetadata()
538
    {
539
        AuthToken token = new AuthToken(sessionId);
540
        CrudService.getInstance().generateMissingSystemMetadata(token);
541
    }
542

    
543
    /**
544
     *  Earthgrid API > Identifier Service > isRegistered Function : 
545
     *  calls MetacatHandler > handleIdIsRegisteredAction
546
     * @param guid
547
     * @throws IOException
548
     */
549
    private void isRegistered(String guid) throws IOException
550
    {
551
        
552
        // Look up the localId for this guid
553
        IdentifierManager im = IdentifierManager.getInstance();
554
        String localId = "";
555
        try {
556
            localId = im.getLocalId(guid);
557
        } catch (McdbDocNotFoundException e) {
558
            // TODO: Need to return the proper DataONE exception
559
        }
560
        
561
        params.put("docid", new String[] { localId });
562
        PrintWriter out = response.getWriter();
563
        response.setStatus(200);
564
        response.setContentType("text/xml");
565
        handler.handleIdIsRegisteredAction(out, params, response);
566
        out.close();
567
    }
568

    
569
    /**
570
     * Earthgrid API > Identifier Service > getAllDocIds Function : 
571
     * calls MetacatHandler > handleGetAllDocidsAction
572
     * @throws IOException
573
     */
574
    private void getAllDocIds() throws IOException {
575
        PrintWriter out = response.getWriter();
576
        response.setStatus(200);
577
        response.setContentType("text/xml");
578
        handler.handleGetAllDocidsAction(out, params, response);
579
        out.close();
580
    }
581

    
582
    /**
583
     * Earthgrid API > Identifier Service > getNextRevision Function : 
584
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
585
     * @param guid
586
     * @throws IOException
587
     */
588
    private void getNextRevision(String guid) throws IOException 
589
    {
590
        params.put("docid", new String[] { guid });
591
        PrintWriter out = response.getWriter();
592
        response.setStatus(200);
593
        response.setContentType("text/xml");
594
        //handler.handleGetRevisionAndDocTypeAction(out, params);
595

    
596
        try {
597
            // Make sure there is a docid
598
            if (guid == null || guid.equals("")) {
599
                throw new Exception("User didn't specify docid!");
600
            }
601

    
602
            // Look up the localId for this guid
603
            IdentifierManager im = IdentifierManager.getInstance();
604
            String localId = "";
605
            try {
606
                localId = im.getLocalId(guid);
607
            } catch (McdbDocNotFoundException e) {
608
                // TODO: Need to return the proper DataONE exception
609
            }
610
           
611
            // Create a DBUtil object
612
            DBUtil dbutil = new DBUtil();
613
            // Get a rev and doctype
614
            String revAndDocType = dbutil
615
                    .getCurrentRevisionAndDocTypeForGivenDocument(localId);
616
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
617

    
618
            out.println("<?xml version=\"1.0\"?>");
619
            out.print("<next-revision>");
620
            out.print(revision);
621
            out.print("</next-revision>");
622

    
623
        } catch (Exception e) {
624
            // Handle exception
625
            response.setStatus(500);
626
            out.println("<?xml version=\"1.0\"?>");
627
            out.println("<error>");
628
            out.println(e.getMessage());
629
            out.println("</error>");
630
        }
631

    
632
        out.close();
633
    }
634

    
635
    /**
636
     * Earthgrid API > Identifier Service > getNextObject Function : 
637
     * calls MetacatHandler > handleGetMaxDocidAction
638
     * @throws IOException
639
     */
640
    private void getNextObject() throws IOException {
641
        PrintWriter out = response.getWriter();
642
        response.setStatus(200);
643
        response.setContentType("text/xml");
644
        handler.handleGetMaxDocidAction(out, params, response);
645
        out.close();
646
    }
647

    
648
    /**
649
     * Implements REST version of DataONE CRUD API --> get
650
     * @param guid ID of data object to be read
651
     */
652
    private void getObject(String guid) {
653
        CrudService cs = CrudService.getInstance();
654
        cs.setParamsFromRequest(request);
655
        AuthToken token = new AuthToken(sessionId);
656
        OutputStream out = null;
657
        try {
658
            out = response.getOutputStream();
659
            response.setStatus(200);
660
            response.setContentType("text/xml");
661
            if(guid != null)
662
            { //get a specific document                
663
                Identifier id = new Identifier();
664
                id.setValue(guid);
665
                try
666
                {
667
                    if(token == null)
668
                    {
669
                        token = new AuthToken("Public");
670
                    }
671
                    InputStream data = cs.get(token, id);
672
                    IOUtils.copyLarge(data, response.getOutputStream());
673
                }
674
                catch(InvalidToken it)
675
                {
676
                    response.setStatus(500);
677
                    serializeException(it, out); 
678
                }
679
                catch(ServiceFailure sf)
680
                {
681
                    response.setStatus(500);
682
                    serializeException(sf, out); 
683
                }
684
                catch(NotAuthorized na)
685
                {
686
                    response.setStatus(500);
687
                    serializeException(na, out); 
688
                }
689
                catch(NotFound nf)
690
                {
691
                    response.setStatus(500);
692
                    serializeException(nf, out); 
693
                }
694
                catch(NotImplemented ni)
695
                {
696
                    response.setStatus(500);
697
                    serializeException(ni, out); 
698
                }
699
                catch(Exception e)
700
                {
701
                    response.setStatus(500);
702
                    System.out.println("Error with Crud.get().  " +
703
                            "If this is an 'Exception producing data' error, " +
704
                            "go to CrudService.get() for better debugging.  " +
705
                            "Here's the error: " + e.getMessage());
706
                    e.printStackTrace();
707
                    ServiceFailure sf = new ServiceFailure("1030", 
708
                            "IO Error in ResourceHandler.getObject: " + e.getMessage());
709
                    serializeException(sf, out); 
710
                }
711
            }
712
            else
713
            { //call listObjects with specified params
714
                Date startTime = null;
715
                Date endTime = null;
716
                ObjectFormat objectFormat = null;
717
                boolean replicaStatus = false;
718
                int start = 0;
719
                //TODO: make the max count into a const
720
                int count = 1000;
721
                Enumeration paramlist = request.getParameterNames();
722
                while (paramlist.hasMoreElements()) 
723
                { //parse the params and make the crud call
724
                    String name = (String) paramlist.nextElement();
725
                    String[] value = (String[])request.getParameterValues(name);
726
                    /*for(int i=0; i<value.length; i++)
727
                    {
728
                        System.out.println("name: " + name + " value: " + value[i]);
729
                    }*/
730
                    if(name.equals("startTime") && value != null)
731
                    {
732
                        try
733
                        {
734
                          //startTime = dateFormat.parse(value[0]);
735
                            startTime = parseDateAndConvertToGMT(value[0]);
736
                        }
737
                        catch(Exception e)
738
                        {  //if we can't parse it, just don't use the startTime param
739
                            System.out.println("Could not parse startTime: " + value[0]);
740
                            startTime = null;
741
                        }
742
                    }
743
                    else if(name.equals("endTime") && value != null)
744
                    {
745
                        try
746
                        {
747
                          //endTime = dateFormat.parse(value[0]);
748
                            endTime = parseDateAndConvertToGMT(value[0]);
749
                        }
750
                        catch(Exception e)
751
                        {  //if we can't parse it, just don't use the endTime param
752
                            System.out.println("Could not parse endTime: " + value[0]);
753
                            endTime = null;
754
                        }
755
                    }
756
                    else if(name.equals("objectFormat") && value != null)
757
                    {
758
                        objectFormat = ObjectFormat.convert(value[0]);
759
                    }
760
                    else if(name.equals("replicaStatus") && value != null)
761
                    {
762
                        if(value != null && 
763
                           value.length > 0 && 
764
                           (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
765
                        {
766
                            replicaStatus = true;
767
                        }
768
                    }
769
                    else if(name.equals("start") && value != null)
770
                    {
771
                        start = new Integer(value[0]).intValue();
772
                    }
773
                    else if(name.equals("count") && value != null)
774
                    {
775
                        count = new Integer(value[0]).intValue();
776
                    }
777
                }
778
                //make the crud call
779
                System.out.println("token: " + token + " startTime: " + startTime +
780
                        " endtime: " + endTime + " objectFormat: " + 
781
                        objectFormat + " replicaStatus: " + replicaStatus + 
782
                        " start: " + start + " count: " + count);
783
               
784
                ObjectList ol = cs.listObjects(token, startTime, endTime, 
785
                        objectFormat, replicaStatus, start, count);
786
                
787
                StringReader sr = new StringReader(ol.toString());                
788
                out = response.getOutputStream();  
789
                response.setStatus(200);
790
                response.setContentType("text/xml");
791
                // Serialize and write it to the output stream
792
                
793
                try {
794
                    serializeServiceType(ObjectList.class, ol, out);
795
                } catch (JiBXException e) {
796
                    throw new ServiceFailure("1190", "Failed to serialize ObjectList: " + e.getMessage());
797
                }
798
            }
799
        } catch (BaseException e) {
800
                response.setStatus(500);
801
                serializeException(e, out);
802
        } catch (IOException e) {
803
            e.printStackTrace();
804
            response.setStatus(500);
805
            ServiceFailure sf = new ServiceFailure("1030", 
806
                    "IO Error in ResourceHandler.getObject: " + e.getMessage());
807
            serializeException(sf, out); 
808
        } catch(NumberFormatException ne) {
809
            response.setStatus(500);
810
            InvalidRequest ir = new InvalidRequest("1030", "Invalid format for parameter: " + ne.getMessage());
811
            serializeException(ir, out);
812
        } catch (Exception e) {
813
            e.printStackTrace();
814
            response.setStatus(500);
815
            ServiceFailure sf = new ServiceFailure("1030", 
816
                    "Exception " + e.getClass().getName() + " raised while handling listObjects request: " + 
817
                    e.getMessage());
818
            serializeException(sf, out);
819
        }
820
    }
821
    
822
    /**
823
     * parse a date and return it in GMT if it ends with a 'Z'
824
     * @param date
825
     * @return
826
     * @throws ParseException
827
     */
828
    private Date parseDateAndConvertToGMT(String date) throws ParseException
829
    {
830
        try
831
        {   //the format we want
832
            return dateFormat.parse(date);
833
        }
834
        catch(java.text.ParseException pe)
835
        {   //try another legacy format
836
            DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
837
            dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
838
            return dateFormat2.parse(date);
839
        }    
840
        
841
        /*System.out.println("Parsing date " + date);
842
        Date d = dateFormat.parse(date);
843
        
844
        if(date.endsWith("Z"))
845
        {
846
            Calendar lTime = Calendar.getInstance();
847
            lTime.setTime(d);
848
            Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
849
            zTime.set(Calendar.MONTH, lTime.get(Calendar.MONTH));
850
            zTime.set(Calendar.DATE, lTime.get(Calendar.DATE));
851
            zTime.set(Calendar.YEAR, lTime.get(Calendar.YEAR));
852
            zTime.set(Calendar.HOUR, lTime.get(Calendar.HOUR));
853
            zTime.set(Calendar.MINUTE, lTime.get(Calendar.MINUTE));
854
            zTime.set(Calendar.SECOND, lTime.get(Calendar.SECOND));
855
            
856
            System.out.println("date parsed to " + zTime.getTime());
857
            return zTime.getTime();
858
        }
859
        
860
        System.out.println("date parsed, but not converted. returned as " + d);
861
        return d;*/
862
    }
863

    
864
    /**
865
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
866
     * @param guid ID of data object to be read
867
     */
868
    private void getSystemMetadataObject(String guid) {
869
        CrudService cs = CrudService.getInstance();
870
        cs.setParamsFromRequest(request);
871
        AuthToken token = new AuthToken(sessionId);
872
        OutputStream out = null;
873
        try {
874
            response.setContentType("text/xml");
875
            response.setStatus(200);
876
            out = response.getOutputStream();
877
            Identifier id = new Identifier();
878
            id.setValue(guid);
879
            SystemMetadata sysmeta = cs.getSystemMetadata(token, id);
880
            
881
            // Serialize and write it to the output stream
882
            try {
883
                //TODO: look at the efficiency of this method.  The system metadata
884
                //is read from metacat (in CrudService) as xml, then serialized
885
                //to a SystemMetadat object, then returned here, then serizlized
886
                //back to XML to be sent to the response.
887
                serializeServiceType(SystemMetadata.class, sysmeta, out);
888
            } catch (JiBXException e) {
889
                throw new ServiceFailure("1190", "Failed to serialize SystemMetadata: " + e.getMessage());
890
            }
891
        } catch (BaseException e) {
892
            response.setStatus(500);
893
                serializeException(e, out);
894
        } catch (IOException e) {
895
            response.setStatus(500);
896
            ServiceFailure sf = new ServiceFailure("1030", 
897
                    "Error in ResourceHandler.getSystemMetadataObject: " + e.getMessage());
898
            serializeException(sf, out);
899
        } finally {
900
            IOUtils.closeQuietly(out);
901
        }
902
    }
903
    
904
    /**
905
     * serialize an object of type to out
906
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
907
     * @param object the object to serialize
908
     * @param out the stream to serialize it to
909
     * @throws JiBXException
910
     */
911
    private void serializeServiceType(Class type, Object object, OutputStream out)
912
      throws JiBXException
913
    {
914
        IBindingFactory bfact = BindingDirectory.getFactory(type);
915
        IMarshallingContext mctx = bfact.createMarshallingContext();
916
        mctx.marshalDocument(object, "UTF-8", null, out);
917
    }
918
    
919
    /**
920
     * deserialize an object of type from is
921
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
922
     * @param is the stream to deserialize from
923
     * @throws JiBXException
924
     */
925
    private Object deserializeServiceType(Class type, InputStream is)
926
      throws JiBXException
927
    {
928
        IBindingFactory bfact = BindingDirectory.getFactory(type);
929
        IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
930
        Object o = (Object) uctx.unmarshalDocument(is, null);
931
        return o;
932
    }
933
    
934
    /**
935
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query 
936
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
937
     * 
938
     * NOTE:
939
     *      This is the only method that uses EcoGrid classes for its implementation.  
940
     *      It does so because it takes an EcoGrid Query as input, and outputs an
941
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
942
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
943
     *      This creates a circular dependency, because the Metacat classes are needed
944
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
945
     *      method.  This circularity could be resolved by moving the EcoGrid classes
946
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
947
     *      favor of these REST interfaces, this circular dependency can be eliminated.
948
     *        
949
     * @throws Exception
950
     */
951
    private void query() throws Exception {
952
        /*  This block commented out because of the EcoGrid circular dependency.
953
         *  For now, query will not be supported until the circularity can be
954
         *  resolved, probably by moving the ecogrid query syntax transformers
955
         *  directly into the Metacat codebase.  MBJ 2010-02-03
956
         
957
        try {
958
            EcogridQueryParser parser = new EcogridQueryParser(request
959
                    .getReader());
960
            parser.parseXML();
961
            QueryType queryType = parser.getEcogridQuery();
962
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
963
                new EcogridJavaToMetacatJavaQueryTransformer();
964
            QuerySpecification metacatQuery = queryTransformer
965
                    .transform(queryType);
966

    
967
            DBQuery metacat = new DBQuery();
968

    
969
            boolean useXMLIndex = (new Boolean(PropertyService
970
                    .getProperty("database.usexmlindex"))).booleanValue();
971
            String xmlquery = "query"; // we don't care the query in resultset,
972
            // the query can be anything
973
            PrintWriter out = null; // we don't want metacat result, so set out null
974

    
975
            // parameter: queryspecification, user, group, usingIndexOrNot
976
            StringBuffer result = metacat.createResultDocument(xmlquery,
977
                    metacatQuery, out, username, groupNames, useXMLIndex);
978

    
979
            // create result set transfer		
980
            String saxparser = PropertyService.getProperty("xml.saxparser");
981
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
982
                    new StringReader(result.toString()), saxparser, queryType
983
                            .getNamespace().get_value());
984
            ResultsetType records = metacatResultsetParser.getEcogridResult();
985

    
986
            System.out
987
                    .println(EcogridResultsetTransformer.toXMLString(records));
988
            response.setContentType("text/xml");
989
            out = response.getWriter();
990
            out.print(EcogridResultsetTransformer.toXMLString(records));
991

    
992
        } catch (Exception e) {
993
            e.printStackTrace();
994
        }*/
995
        response.setContentType("text/xml");
996
        response.setStatus(501);
997
        PrintWriter out = response.getWriter();
998
        out.print("<error>Query operation not yet supported by Metacat.</error>");
999
        out.close();
1000
    }
1001
    
1002
    private String streamToString(InputStream is)
1003
    throws IOException
1004
    {
1005
        return IOUtil.toString(is);
1006
    }
1007

    
1008
    private InputStream stringToStream(String s)
1009
    throws IOException
1010
    {
1011
        ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
1012
        return bais;
1013
    }
1014
    
1015
    /**
1016
     * process a mime multipart message
1017
     * @param is
1018
     * @return
1019
     */
1020
    /*private Hashtable processMMP(InputStream is)
1021
      throws IOException
1022
    {
1023
        //TODO: verify that this is how the RFC for MMP should work
1024
        //SORTAHACK: Since mmp seems to have a bug where large object parts get truncated,
1025
        //parse the stream here.  This has the disavantage of putting the
1026
        //stream into memory.
1027
        InputStream object = null;
1028
        InputStream sysmeta = null;
1029
        String s = IOUtils.toString(is);
1030
        System.out.println("mime: " + s);
1031
        //figure out what the boundary marker is
1032
        String searchString = "boundary=";
1033
        int searchStringIndex = s.indexOf(searchString);
1034
        String boundary = s.substring(searchStringIndex + searchString.length() + 1, 
1035
                s.indexOf("\"", searchStringIndex + searchString.length() + 1));
1036
        boundary = "--" + boundary;
1037
        //System.out.println("boundary is " + boundary);
1038
        
1039
        //find the system metadata
1040
        searchString = "Content-Disposition: attachment; filename=systemmetadata";
1041
        searchStringIndex = s.indexOf(searchString);
1042
        String sm = s.substring(searchStringIndex +
1043
                searchString.length() + 2, 
1044
                s.indexOf(boundary, searchStringIndex));
1045
        sysmeta = new ByteArrayInputStream(sm.getBytes());
1046
        
1047
        //find the object
1048
        searchString = "Content-Disposition: attachment; filename=object";
1049
        searchStringIndex = s.indexOf(searchString);
1050
        String o = s.substring(searchStringIndex +
1051
                searchString.length() + 2, 
1052
                s.indexOf(boundary, searchStringIndex));
1053
        object = new ByteArrayInputStream(o.getBytes());
1054
        
1055
        Hashtable h = new Hashtable();
1056
        h.put("object", object);
1057
        h.put("systemmetadata", sysmeta);
1058
                
1059
        //System.out.println("o: \"" + o + "\"");
1060
        //System.out.println("sm: \"" + sm + "\"");
1061
        return h;
1062
    }
1063
    */
1064
    
1065
    protected static String[] findBoundaryString(InputStream is)
1066
        throws IOException
1067
    {
1068
        String[] endResult = new String[2];
1069
        String boundary = "";
1070
        String searchString = "boundary=";
1071
        boolean doneWithCurrentArray = false;
1072
        byte[] b = new byte[1024];
1073
        int numbytes = is.read(b, 0, 1024);
1074
        while(numbytes != -1)
1075
        {
1076
            String s = new String(b, 0, numbytes);
1077
            
1078
            int[] result = StreamUtil.lookForMatch(searchString, s);
1079
            int searchStringIndex = s.indexOf(searchString);
1080
            if(s.indexOf("\"", searchStringIndex + searchString.length() + 1) == -1)
1081
            { //the end of the boundary is in the next byte array
1082
                boundary = s.substring(searchStringIndex + searchString.length() + 1, s.length());
1083
            }
1084
            else if(!boundary.startsWith("--"))
1085
            { //we can read the whole boundary from this byte array
1086
                boundary = s.substring(searchStringIndex + searchString.length() + 1, 
1087
                    s.indexOf("\"", searchStringIndex + searchString.length() + 1));
1088
                boundary = "--" + boundary;
1089
                endResult[0] = boundary;
1090
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
1091
                        s.length());
1092
                break;
1093
            }
1094
            else
1095
            { //we're now reading the 2nd byte array to get the rest of the boundary
1096
                searchString = "\"";
1097
                searchStringIndex = s.indexOf(searchString);
1098
                boundary += s.substring(0, searchStringIndex);
1099
                boundary = "--" + boundary;
1100
                endResult[0] = boundary;
1101
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
1102
                        s.length());
1103
                break;
1104
            }
1105
        }
1106
        System.out.println("boundary is: " + boundary);
1107
        return endResult;
1108
    }
1109
    
1110
    protected String writeMMPPartToFile(String beginSearch, 
1111
            InputStream is, String boundary, String searchString, File f)
1112
        throws IOException
1113
    {
1114
        Logger logMetacat = Logger.getLogger(ResourceHandler.class);
1115
        logMetacat.info("writing MMP parts");
1116
        String s = beginSearch;
1117
        FileOutputStream fos = new FileOutputStream(f);
1118
        int numread = 0;
1119
        byte[] b = new byte[1024];
1120
        String writeString = "";
1121
        
1122
        if(s == null)
1123
        {   //starting with the first part of the stream 
1124
            numread = is.read(b, 0, 1024);
1125
            s = new String(b, 0, numread);
1126
        }
1127
        
1128
        boolean useCurrentS = true;
1129
        boolean searchForBoundary = false;
1130
        String seekString = searchString;
1131
        
1132
        while(numread != -1)
1133
        {
1134
            logMetacat.info("////////////////////////iterating");
1135
            logMetacat.info("searchForBoundary: " + searchForBoundary);
1136
            logMetacat.info("useCurrentS: " + useCurrentS);
1137
            if(searchForBoundary)
1138
            {
1139
                seekString = boundary;
1140
            }
1141
            else
1142
            {
1143
                seekString = searchString;
1144
            }
1145
            
1146
            int[] result = StreamUtil.lookForMatch(seekString, s);
1147
            if(!useCurrentS)
1148
            {
1149
                numread = is.read(b, 0, 1024);
1150
                if(numread != -1)
1151
                {
1152
                    s = new String(b, 0, numread);
1153
                }
1154
                else
1155
                {
1156
                    break;
1157
                }
1158
            }
1159
            
1160
            logMetacat.info("seekString: " + seekString);
1161
            logMetacat.info("in string: " + s);
1162
            
1163
            if(result[0] >= 0 && result[1] == seekString.length())
1164
            {
1165
                //searchString is full in s
1166
                logMetacat.info("seekstring is fully in s");
1167
                if(!searchForBoundary)
1168
                {   //we're looking for searchString and we found it
1169
                    //chop off the searchString itself and start writing
1170
                    //until we find boundary
1171
                    s = s.substring(result[0] + result[1], s.length());
1172
                    if(s.length() > 0)
1173
                    {
1174
                        useCurrentS = true;
1175
                    }
1176
                    else
1177
                    {
1178
                        useCurrentS = false;
1179
                    }
1180
                    searchForBoundary = true;
1181
                }
1182
                else
1183
                {   //we're writing, but we found the boundary in this chunk
1184
                    
1185
                    writeString = s.substring(0, result[0]);
1186
                    System.out.println("writing1: " + writeString);
1187
                    fos.write(writeString.getBytes());
1188
                    //we're done.  break and return;
1189
                    return s.substring(result[0] + result[1], s.length());
1190
                }
1191
            }
1192
            else if(result[0] > 0 && result[1] != seekString.length())
1193
            {
1194
                logMetacat.info("seekstring is partially in s");
1195
                //seekString is partially in s
1196
                //more specifically, the beginning of seekString is at the end
1197
                //of s
1198
                
1199
                //get the next chunk right now, see if the beginning matches
1200
                numread = is.read(b, 0, 1024);
1201
                String s2 = new String(b, 0, numread);
1202
                s += s2;
1203
                useCurrentS = true;
1204
            }
1205
            else
1206
            {
1207
                logMetacat.info("seekstring is not in s");
1208
                //searchString is not in s 
1209
                if(searchForBoundary)
1210
                {
1211
                    System.out.println("writing2: " + s);
1212
                    fos.write(s.getBytes());
1213
                }
1214
                numread = is.read(b, 0, 1024);
1215
                if(numread != -1)
1216
                {
1217
                    s = new String(b, 0, numread);
1218
                }
1219
                else
1220
                {
1221
                    break;
1222
                }
1223
                useCurrentS = true;
1224
            }
1225
        }
1226
        return "";
1227
    }
1228
    
1229
    protected Hashtable<String, File> writeMMPPartsToFiles(InputStream is)
1230
        throws IOException
1231
    {
1232
        Logger logMetacat = Logger.getLogger(ResourceHandler.class);
1233
        logMetacat.info("Processing Mime Multipart");
1234
        String[] boundaryResults = findBoundaryString(is);
1235
        String boundary = boundaryResults[0];
1236
        String s = boundaryResults[1];
1237
        String[] searchStrings = {
1238
                "Content-Disposition: attachment; filename=systemmetadata\n\n",
1239
                "Content-Disposition: attachment; filename=object\n\n"};
1240
        
1241
        File[] fileArr = getMMPTempFiles();
1242
        Hashtable<String, File> h = new Hashtable<String, File>();
1243
        //System.out.println("==========================Looking for SM");
1244
        //System.out.println("writing sm to " + fileArr[0].getAbsolutePath());
1245
        logMetacat.info("writing mime system metadata to " + fileArr[0].getAbsolutePath());
1246
        s = writeMMPPartToFile(s, is, boundary, searchStrings[0], fileArr[0]);
1247
        //System.out.println("==========================Looking for Object");
1248
        //System.out.println("writing obj to " + fileArr[1].getAbsolutePath());
1249
        logMetacat.info("writing mime object to " + fileArr[1].getAbsolutePath());
1250
        writeMMPPartToFile(s, is, boundary, searchStrings[1], fileArr[1]);
1251
        h.put("sysmeta", fileArr[0]);
1252
        h.put("object", fileArr[1]);
1253
        return h;
1254
    }
1255
    
1256
    private static File[] getMMPTempFiles()
1257
        throws IOException
1258
    {
1259
        Logger logMetacat = Logger.getLogger(ResourceHandler.class);
1260
        File tmpDir;
1261
        try
1262
        {
1263
            tmpDir = new File(PropertyService.getProperty("application.tempDir"));
1264
        }
1265
        catch(PropertyNotFoundException pnfe)
1266
        {
1267
            logMetacat.error("ResourceHandler.writeMMPPartstoFiles: " +
1268
                    "application.tmpDir not found.  Using /tmp instead.");
1269
            tmpDir = new File("/tmp");
1270
        }
1271
        long datetimetag = new Date().getTime();
1272
        File smFile = new File(tmpDir, "sm." + datetimetag + ".tmp");
1273
        File objFile = new File(tmpDir, "obj." + datetimetag + ".tmp");
1274
        File[] fileArr = {smFile, objFile};
1275
        return fileArr;
1276
    }
1277
    
1278
    /**
1279
     * return a vector where the first element is a string that represents the system
1280
     * metadata and the 2nd element is an InputStream that is the object
1281
     */
1282
    protected Hashtable<String, File> processMMP(final InputStream is)
1283
      throws IOException
1284
    {
1285
        return writeMMPPartsToFiles(is);
1286
    }
1287
    
1288
    /**
1289
     * Earthgrid API > Put Service >Put Function : calls MetacatHandler > handleInsertOrUpdateAction 
1290
     * 
1291
     * @param guid ID of data object to be inserted or updated
1292
     * @throws IOException
1293
     */
1294
    private void putObject(String guid, String action) {
1295
        logMetacat.debug("Entering putObject: " + guid + "/" + action);
1296
        OutputStream out = null;
1297
        try {
1298
            out = response.getOutputStream();
1299
            response.setStatus(200);
1300
            response.setContentType("text/xml");
1301
        } catch (IOException e1) {
1302
            logMetacat.error("Could not get the output stream for writing in putObject");
1303
        }
1304
        try {
1305
            
1306
            // Read the incoming data from its Mime Multipart encoding
1307
            logMetacat.debug("Disassembling MIME multipart form");
1308
            InputStream object = null;
1309
            InputStream sysmeta = null;
1310
            Hashtable<String, File> parts;
1311
            
1312
            try
1313
            {
1314
                parts = processMMP(request.getInputStream());
1315
                object = new FileInputStream(parts.get("object"));
1316
                sysmeta = new FileInputStream(parts.get("sysmeta"));
1317
            }
1318
            catch(IOException ioe)
1319
            {
1320
                throw new ServiceFailure("1202", 
1321
                        "IOException when processing Mime Multipart: " + ioe.getMessage());
1322
            }
1323
            
1324
            if ( action.equals(FUNCTION_NAME_INSERT)) { //handle inserts
1325

    
1326
                // Check if the objectId exists
1327
                IdentifierManager im = IdentifierManager.getInstance();
1328
                if (im.identifierExists(guid)) {
1329
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
1330
                }
1331

    
1332
                logMetacat.debug("Commence creation...");
1333
                IBindingFactory bfact =
1334
                    BindingDirectory.getFactory(SystemMetadata.class);
1335
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1336
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1337

    
1338
                CrudService cs = CrudService.getInstance();
1339
                AuthToken token = new AuthToken(sessionId); 
1340
                cs.setParamsFromRequest(request);
1341
                Identifier id = new Identifier();
1342
                id.setValue(guid);
1343
                cs.create(token, id, object, m);
1344
                
1345
            } else if (action.equals(FUNCTION_NAME_UPDATE)) { //handle updates
1346
                IdentifierManager im = IdentifierManager.getInstance();
1347
                CrudService cs = CrudService.getInstance();
1348
                Identifier obsoletedGuid = new Identifier();
1349
                Identifier id = new Identifier();
1350
                id.setValue(guid);
1351
                AuthToken token = new AuthToken(sessionId);
1352
                
1353
                //do some checks
1354
                if(params.get("obsoletedGuid") == null)
1355
                {
1356
                    throw new InvalidRequest("1202", "obsoletedGuid must be contained in the request parameters.");
1357
                }
1358
                //get the obsoletedGuid
1359
                String[] obsGuidS = params.get("obsoletedGuid");
1360
                obsoletedGuid.setValue(obsGuidS[0]);
1361
                
1362
                if (!im.identifierExists(obsoletedGuid.getValue())) 
1363
                {
1364
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1365
                }
1366
                
1367
                
1368
                logMetacat.debug("Commence update...");
1369
                
1370
                //get the systemmetadata
1371
                IBindingFactory bfact =
1372
                        BindingDirectory.getFactory(SystemMetadata.class);
1373
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1374
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1375
                
1376
                //do the update
1377
                try
1378
                {
1379
                    cs.setParamsFromRequest(request);
1380
                    Identifier rId = cs.update(token, id, object, obsoletedGuid, m);
1381
                }
1382
                catch(NotFound e)
1383
                {
1384
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1385
                }
1386
                
1387
            } else {
1388
                throw new InvalidRequest("1000", "Operation must be create or update.");
1389
            }
1390
            
1391
            //clean up the MMP files
1392
            //parts.get("sysmeta").delete();
1393
            //parts.get("object").delete();
1394
        } catch (NotAuthorized e) {
1395
            response.setStatus(500);
1396
            serializeException(e, out);
1397
        } catch (InvalidToken e) {
1398
            response.setStatus(500);
1399
            serializeException(e, out);
1400
        } catch (ServiceFailure e) {
1401
            response.setStatus(500);
1402
            serializeException(e, out);
1403
        } catch (IdentifierNotUnique e) {
1404
            response.setStatus(500);
1405
            serializeException(e, out);
1406
        } catch (UnsupportedType e) {
1407
            response.setStatus(500);
1408
            serializeException(e, out);
1409
        } catch (InsufficientResources e) {
1410
            response.setStatus(500);
1411
            serializeException(e, out);
1412
        } catch (InvalidSystemMetadata e) {
1413
            response.setStatus(500);
1414
            serializeException(e, out);
1415
        } catch (NotImplemented e) {
1416
            response.setStatus(500);
1417
            serializeException(e, out);
1418
        } catch (InvalidRequest e) {
1419
            response.setStatus(500);
1420
            serializeException(e, out);
1421
        } /*catch (MessagingException e) {
1422
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1423
            serializeException(sf, out);
1424
        } catch (IOException e) {
1425
            response.setStatus(500);
1426
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1427
            serializeException(sf, out);
1428
        }*/ catch (JiBXException e) {
1429
            response.setStatus(500);
1430
            e.printStackTrace(System.out);
1431
            InvalidSystemMetadata ism = new InvalidSystemMetadata("1080", e.getMessage());
1432
            serializeException(ism, out);
1433
        }
1434
    }
1435

    
1436
    /**
1437
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction  
1438
     * 
1439
     * @param guid ID of data object to be deleted
1440
     * @throws IOException
1441
     */
1442
    private void deleteObject(String guid) throws IOException 
1443
    {
1444
        // Look up the localId for this global identifier
1445
        IdentifierManager im = IdentifierManager.getInstance();
1446
        String localId = "";
1447
        try {
1448
            localId = im.getLocalId(guid);
1449
        } catch (McdbDocNotFoundException e) {
1450
            // TODO: Need to return the proper DataONE exception
1451
        }
1452
        
1453
        params.put("docid", new String[] { localId });
1454
        PrintWriter out = response.getWriter();
1455
        response.setStatus(200);
1456
        response.setContentType("text/xml");
1457
        handler.handleDeleteAction(out, params, request, response, username,
1458
                groupNames);
1459
        out.close();
1460
    }
1461
    
1462
    /**
1463
     * set the access perms on a document
1464
     * @throws IOException
1465
     */
1466
    private void setaccess() throws Exception
1467
    {
1468
        try
1469
        {
1470
            String guid = params.get("guid")[0];
1471
            Identifier id = new Identifier();
1472
            id.setValue(guid);
1473
            AuthToken token = new AuthToken(sessionId);
1474
            String principal = params.get("principal")[0];
1475
            String permission = params.get("permission")[0];
1476
            String permissionType = params.get("permissionType")[0];
1477
            String permissionOrder = params.get("permissionOrder")[0];
1478
            String setSystemMetadata = params.get("setsystemmetadata")[0];
1479
            boolean ssm = false;
1480
            if(setSystemMetadata.equals("true") || setSystemMetadata.equals("TRUE") ||
1481
                    setSystemMetadata.equals("yes"))
1482
            {
1483
                ssm = true;
1484
            }
1485
            CrudService cs = CrudService.getInstance();
1486
            //TODO: remove the setsystemmetadata param and set this so the systemmetadata always gets set
1487
            cs.setAccess(token, id, principal, permission, permissionType, permissionOrder, ssm);
1488
        }
1489
        catch(Exception e)
1490
        {
1491
            response.setStatus(500);
1492
            printError("Error setting access in ResourceHandler: " + e.getMessage(), response);
1493
            throw e;
1494
        }
1495
    }
1496

    
1497
    /**
1498
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
1499
     * 
1500
     * @throws IOException
1501
     */
1502
    private void login() throws IOException {
1503
        PrintWriter out = response.getWriter();
1504
        response.setStatus(200);
1505
        response.setContentType("text/xml");
1506
        handler.handleLoginAction(out, params, request, response);
1507
        out.close();
1508
    }
1509

    
1510
    /**
1511
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
1512
     * 
1513
     * @throws IOException
1514
     */
1515
    private void logout() throws IOException {
1516
        PrintWriter out = response.getWriter();
1517
        response.setStatus(200);
1518
        response.setContentType("text/xml");
1519
        handler.handleLogoutAction(out, params, request, response);
1520
        out.close();
1521
    }
1522

    
1523
    /**
1524
     * Prints xml response
1525
     * @param message Message to be displayed
1526
     * @param response Servlet response that xml message will be printed 
1527
     * */
1528
    private void printError(String message, HttpServletResponse response) {
1529
        try {
1530
            logMetacat.error("ResourceHandler: Printing error to servlet response: " + message);
1531
            PrintWriter out = response.getWriter();
1532
            response.setContentType("text/xml");
1533
            out.println("<?xml version=\"1.0\"?>");
1534
            out.println("<error>");
1535
            out.println(message);
1536
            out.println("</error>");
1537
            out.close();
1538
        } catch (IOException e) {
1539
            e.printStackTrace();
1540
        }
1541
    }
1542
    
1543
    private void serializeException(BaseException e, OutputStream out) {
1544
        // TODO: Use content negotiation to determine which return format to use
1545
        response.setContentType("text/xml");
1546
        response.setStatus(e.getCode());
1547
        
1548
        logMetacat.error("ResourceHandler: Serializing exception with code " + e.getCode() + ": " + e.getMessage());
1549
        e.printStackTrace();
1550
        
1551
        try {
1552
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
1553
        } catch (IOException e1) {
1554
            logMetacat.error("Error writing exception to stream. " 
1555
                    + e1.getMessage());
1556
        }
1557
    }
1558

    
1559
}
(2-2/3)