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
    private static final String RESOURCE_CHECKSUM = "checksum";
207

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
384
            	} else if(resource.equals(RESOURCE_CHECKSUM)) {
385
            	    //handle checksum requests
386
            	    System.out.println("Handling getChecksum request");
387
            	    if(httpVerb == GET)
388
            	    {
389
            	        String guid = null;
390
            	        String checksumAlgorithm = "MD5";
391
            	    
392
            	        try
393
            	        {
394
            	           guid = params.get("id")[0];
395
            	        }
396
            	        catch(Exception e)
397
            	        {
398
            	            throw new InvalidRequest("1402", "Incorrect parameters passed to getChecksum");
399
            	        }
400
            	        
401
            	        Identifier guidid = new Identifier();
402
            	        guidid.setValue(guid);
403
            	        AuthToken token = new AuthToken(sessionId);
404
            	        try
405
            	        {
406
            	            checksumAlgorithm = params.get("checksumAlgorithm")[0];
407
            	        }
408
            	        catch(Exception e)
409
            	        {
410
            	            //do nothing.  default to MD5
411
            	        }
412
            	        System.out.println("getting checksum for object " + guid +
413
            	                " with algorithm " + checksumAlgorithm);
414
            	        try
415
            	        {
416
            	            Checksum c = CrudService.getInstance().getChecksum(token, guidid, checksumAlgorithm);
417
            	            System.out.println("got checksum " + c.getValue());
418
            	            response.setStatus(200);
419
            	            System.out.println("serializing response");
420
            	            serializeServiceType(Checksum.class, c, response.getOutputStream());
421
            	            System.out.println("done serializing response.");
422
            	        }
423
            	        catch(NotAuthorized na)
424
            	        {
425
            	            na.setDetail_code("1400");
426
            	            serializeException(na, response.getOutputStream());
427
            	        }
428
            	        catch(NotFound nf)
429
            	        {
430
            	            nf.setDetail_code("1420");
431
            	            serializeException(nf, response.getOutputStream());
432
            	        }
433
            	        catch(InvalidRequest ir)
434
            	        {
435
            	            ir.setDetail_code("1402");
436
            	            serializeException(ir, response.getOutputStream());
437
            	        }
438
            	        catch(ServiceFailure sf)
439
            	        {
440
            	            sf.setDetail_code("1410");
441
            	            serializeException(sf, response.getOutputStream());
442
            	        }
443
            	        catch(InvalidToken it)
444
            	        {
445
            	            it.setDetail_code("1430");
446
            	            serializeException(it, response.getOutputStream());
447
            	        }
448
            	    }
449
            	}
450
            	    
451
            	if (!status)
452
            	{
453
            	    response.setStatus(400);
454
            		printError("Incorrect parameters!", response);
455
            	}
456
            } else {
457
                response.setStatus(400);
458
            	printError("Incorrect resource!", response);
459
            }
460
        } catch (Exception e) {
461
        	logMetacat.error(e.getMessage());
462
        	e.printStackTrace();
463
        }
464
    }
465
    
466
    /**
467
     * get the logs from the CrudService based on passed params.  Available 
468
     * params are token, fromDate, toDate, event.  See 
469
     * http://mule1.dataone.org/ArchitectureDocs/mn_api_crud.html#MN_crud.getLogRecords
470
     * for more info
471
     */
472
    private void getLog()
473
    {
474
        OutputStream out = null;
475
        try
476
        {
477
            out = response.getOutputStream();
478
            response.setStatus(200);
479
            response.setContentType("text/xml");
480
            AuthToken token = new AuthToken(sessionId);
481
            String fromDateS = params.get("fromDate")[0];
482
            System.out.println("param fromDateS: " + fromDateS);
483
            Date fromDate = null;
484
            String toDateS = params.get("toDate")[0];
485
            System.out.println("param toDateS: " + toDateS);
486
            Date toDate = null;
487
            String eventS = params.get("event")[0];
488
            Event event = null;
489
            if(fromDateS != null)
490
            {
491
                //fromDate = dateFormat.parse(fromDateS);
492
                fromDate = parseDateAndConvertToGMT(fromDateS);
493
            }
494
            if(toDateS != null)
495
            {
496
                //toDate = dateFormat.parse(toDateS);
497
                toDate = parseDateAndConvertToGMT(toDateS);
498
            }
499
            if(eventS != null)
500
            {
501
                event = Event.convert(eventS);
502
            }
503
            System.out.println("fromDate: " + fromDate + " toDate: " + toDate);
504
            
505
            System.out.println("calling crudservice.getLogRecords");
506
            Log log = CrudService.getInstance().getLogRecords(token, fromDate, toDate, event);
507
            serializeServiceType(Log.class, log, out);
508
        }
509
        catch(Exception e)
510
        {
511
            String msg = "Could not get logs from CrudService: " + e.getMessage();
512
            response.setStatus(500);
513
            ServiceFailure sf = new ServiceFailure("1490", msg);
514
            logMetacat.error(msg);
515
            e.printStackTrace();
516
            serializeException(sf, out);
517
        }
518
    }
519
    
520
    /**
521
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions  
522
     */
523
    private void initParams() {
524

    
525
        String name = null;
526
        String[] value = null;
527
        Enumeration paramlist = request.getParameterNames();
528
        while (paramlist.hasMoreElements()) {
529
            name = (String) paramlist.nextElement();
530
            value = request.getParameterValues(name);
531
            params.put(name, value);
532
        }
533
    }
534

    
535
    /**
536
     * 
537
     * Load user details of metacat session from the request 
538
     * 
539
     */
540
    private void loadSessionData()
541
      throws Exception
542
    {
543
        SessionData sessionData = RequestUtil.getSessionData(request);
544
        try
545
        {
546
            username = null;
547
            password = null;
548
            groupNames = null;
549
            sessionId = null;
550
            
551
            boolean validSession = false;
552
            SessionService ss = SessionService.getInstance();
553
            System.out.println("sessionData: " + sessionData);
554
            if(sessionData == null)
555
            {
556
                username = "public";
557
                sessionId = "0";
558
                System.out.println("sessiondata is null.  Creating a public session.");
559
                return;
560
            }
561
            
562
            System.out.println("username: " + sessionData.getUserName());
563
            System.out.println("sessionid: " + sessionData.getId());
564
            //validate the session
565
            if(ss.isSessionRegistered(sessionData.getId()) && 
566
               !(sessionData.getUserName().equals("public") || sessionData.getId().equals("0")))
567
            {
568
                validSession = true;
569
            }
570
            
571
            if(validSession)
572
            {
573
                //if the session is valid, set these variables
574
                username = sessionData.getUserName();
575
                password = sessionData.getPassword();
576
                groupNames = sessionData.getGroupNames();
577
                sessionId = sessionData.getId();
578
                System.out.println("setting sessionid to " + sessionId);
579
                System.out.println("username: " + username);
580
            }
581
            
582
            //if the session is not valid or the username is null, set
583
            //username to public
584
            if (username == null) 
585
            {
586
                System.out.println("setting username to public.");
587
                username = "public";
588
            }
589
        }
590
        catch(Exception e)
591
        {
592
            e.printStackTrace();
593
            throw new Exception("Could not load the session data: " + e.getMessage());
594
        }
595
    }
596
    
597
    /**
598
     * generate missing system metadata for any science metadata objects
599
     * that don't already have it. https://trac.dataone.org/ticket/591
600
     * 
601
     * called with POST meta/?op=generatemissingsystemmetadata
602
     */
603
    private void generateMissingSystemMetadata()
604
    {
605
        AuthToken token = new AuthToken(sessionId);
606
        CrudService.getInstance().generateMissingSystemMetadata(token);
607
    }
608

    
609
    /**
610
     *  Earthgrid API > Identifier Service > isRegistered Function : 
611
     *  calls MetacatHandler > handleIdIsRegisteredAction
612
     * @param guid
613
     * @throws IOException
614
     */
615
    private void isRegistered(String guid) throws IOException
616
    {
617
        
618
        // Look up the localId for this guid
619
        IdentifierManager im = IdentifierManager.getInstance();
620
        String localId = "";
621
        try {
622
            localId = im.getLocalId(guid);
623
        } catch (McdbDocNotFoundException e) {
624
            // TODO: Need to return the proper DataONE exception
625
        }
626
        
627
        params.put("docid", new String[] { localId });
628
        PrintWriter out = response.getWriter();
629
        response.setStatus(200);
630
        response.setContentType("text/xml");
631
        handler.handleIdIsRegisteredAction(out, params, response);
632
        out.close();
633
    }
634

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

    
648
    /**
649
     * Earthgrid API > Identifier Service > getNextRevision Function : 
650
     * calls MetacatHandler > handleGetRevisionAndDocTypeAction
651
     * @param guid
652
     * @throws IOException
653
     */
654
    private void getNextRevision(String guid) throws IOException 
655
    {
656
        params.put("docid", new String[] { guid });
657
        PrintWriter out = response.getWriter();
658
        response.setStatus(200);
659
        response.setContentType("text/xml");
660
        //handler.handleGetRevisionAndDocTypeAction(out, params);
661

    
662
        try {
663
            // Make sure there is a docid
664
            if (guid == null || guid.equals("")) {
665
                throw new Exception("User didn't specify docid!");
666
            }
667

    
668
            // Look up the localId for this guid
669
            IdentifierManager im = IdentifierManager.getInstance();
670
            String localId = "";
671
            try {
672
                localId = im.getLocalId(guid);
673
            } catch (McdbDocNotFoundException e) {
674
                // TODO: Need to return the proper DataONE exception
675
            }
676
           
677
            // Create a DBUtil object
678
            DBUtil dbutil = new DBUtil();
679
            // Get a rev and doctype
680
            String revAndDocType = dbutil
681
                    .getCurrentRevisionAndDocTypeForGivenDocument(localId);
682
            int revision = Integer.parseInt(revAndDocType.split(";")[0]) + 1;
683

    
684
            out.println("<?xml version=\"1.0\"?>");
685
            out.print("<next-revision>");
686
            out.print(revision);
687
            out.print("</next-revision>");
688

    
689
        } catch (Exception e) {
690
            // Handle exception
691
            response.setStatus(500);
692
            out.println("<?xml version=\"1.0\"?>");
693
            out.println("<error>");
694
            out.println(e.getMessage());
695
            out.println("</error>");
696
        }
697

    
698
        out.close();
699
    }
700

    
701
    /**
702
     * Earthgrid API > Identifier Service > getNextObject Function : 
703
     * calls MetacatHandler > handleGetMaxDocidAction
704
     * @throws IOException
705
     */
706
    private void getNextObject() throws IOException {
707
        PrintWriter out = response.getWriter();
708
        response.setStatus(200);
709
        response.setContentType("text/xml");
710
        handler.handleGetMaxDocidAction(out, params, response);
711
        out.close();
712
    }
713

    
714
    /**
715
     * Implements REST version of DataONE CRUD API --> get
716
     * @param guid ID of data object to be read
717
     */
718
    private void getObject(String guid) {
719
        CrudService cs = CrudService.getInstance();
720
        cs.setParamsFromRequest(request);
721
        AuthToken token = new AuthToken(sessionId);
722
        OutputStream out = null;
723
        try {
724
            out = response.getOutputStream();
725
            response.setStatus(200);
726
            response.setContentType("text/xml");
727
            if(guid != null)
728
            { //get a specific document                
729
                Identifier id = new Identifier();
730
                id.setValue(guid);
731
                try
732
                {
733
                    if(token == null)
734
                    {
735
                        token = new AuthToken("Public");
736
                    }
737
                    InputStream data = cs.get(token, id);
738
                    IOUtils.copyLarge(data, response.getOutputStream());
739
                }
740
                catch(InvalidToken it)
741
                {
742
                    response.setStatus(500);
743
                    serializeException(it, out); 
744
                }
745
                catch(ServiceFailure sf)
746
                {
747
                    response.setStatus(500);
748
                    serializeException(sf, out); 
749
                }
750
                catch(NotAuthorized na)
751
                {
752
                    response.setStatus(500);
753
                    serializeException(na, out); 
754
                }
755
                catch(NotFound nf)
756
                {
757
                    response.setStatus(500);
758
                    serializeException(nf, out); 
759
                }
760
                catch(NotImplemented ni)
761
                {
762
                    response.setStatus(500);
763
                    serializeException(ni, out); 
764
                }
765
                catch(Exception e)
766
                {
767
                    response.setStatus(500);
768
                    System.out.println("Error with Crud.get().  " +
769
                            "If this is an 'Exception producing data' error, " +
770
                            "go to CrudService.get() for better debugging.  " +
771
                            "Here's the error: " + e.getMessage());
772
                    e.printStackTrace();
773
                    ServiceFailure sf = new ServiceFailure("1030", 
774
                            "IO Error in ResourceHandler.getObject: " + e.getMessage());
775
                    serializeException(sf, out); 
776
                }
777
            }
778
            else
779
            { //call listObjects with specified params
780
                Date startTime = null;
781
                Date endTime = null;
782
                ObjectFormat objectFormat = null;
783
                boolean replicaStatus = false;
784
                int start = 0;
785
                //TODO: make the max count into a const
786
                int count = 1000;
787
                Enumeration paramlist = request.getParameterNames();
788
                while (paramlist.hasMoreElements()) 
789
                { //parse the params and make the crud call
790
                    String name = (String) paramlist.nextElement();
791
                    String[] value = (String[])request.getParameterValues(name);
792
                    /*for(int i=0; i<value.length; i++)
793
                    {
794
                        System.out.println("name: " + name + " value: " + value[i]);
795
                    }*/
796
                    if(name.equals("startTime") && value != null)
797
                    {
798
                        try
799
                        {
800
                          //startTime = dateFormat.parse(value[0]);
801
                            startTime = parseDateAndConvertToGMT(value[0]);
802
                        }
803
                        catch(Exception e)
804
                        {  //if we can't parse it, just don't use the startTime param
805
                            System.out.println("Could not parse startTime: " + value[0]);
806
                            startTime = null;
807
                        }
808
                    }
809
                    else if(name.equals("endTime") && value != null)
810
                    {
811
                        try
812
                        {
813
                          //endTime = dateFormat.parse(value[0]);
814
                            endTime = parseDateAndConvertToGMT(value[0]);
815
                        }
816
                        catch(Exception e)
817
                        {  //if we can't parse it, just don't use the endTime param
818
                            System.out.println("Could not parse endTime: " + value[0]);
819
                            endTime = null;
820
                        }
821
                    }
822
                    else if(name.equals("objectFormat") && value != null)
823
                    {
824
                        objectFormat = ObjectFormat.convert(value[0]);
825
                    }
826
                    else if(name.equals("replicaStatus") && value != null)
827
                    {
828
                        if(value != null && 
829
                           value.length > 0 && 
830
                           (value[0].equals("true") || value[0].equals("TRUE") || value[0].equals("YES")))
831
                        {
832
                            replicaStatus = true;
833
                        }
834
                    }
835
                    else if(name.equals("start") && value != null)
836
                    {
837
                        start = new Integer(value[0]).intValue();
838
                    }
839
                    else if(name.equals("count") && value != null)
840
                    {
841
                        count = new Integer(value[0]).intValue();
842
                    }
843
                }
844
                //make the crud call
845
                System.out.println("token: " + token + " startTime: " + startTime +
846
                        " endtime: " + endTime + " objectFormat: " + 
847
                        objectFormat + " replicaStatus: " + replicaStatus + 
848
                        " start: " + start + " count: " + count);
849
               
850
                ObjectList ol = cs.listObjects(token, startTime, endTime, 
851
                        objectFormat, replicaStatus, start, count);
852
                
853
                StringReader sr = new StringReader(ol.toString());                
854
                out = response.getOutputStream();  
855
                response.setStatus(200);
856
                response.setContentType("text/xml");
857
                // Serialize and write it to the output stream
858
                
859
                try {
860
                    serializeServiceType(ObjectList.class, ol, out);
861
                } catch (JiBXException e) {
862
                    throw new ServiceFailure("1190", "Failed to serialize ObjectList: " + e.getMessage());
863
                }
864
            }
865
        } catch (BaseException e) {
866
                response.setStatus(500);
867
                serializeException(e, out);
868
        } catch (IOException e) {
869
            e.printStackTrace();
870
            response.setStatus(500);
871
            ServiceFailure sf = new ServiceFailure("1030", 
872
                    "IO Error in ResourceHandler.getObject: " + e.getMessage());
873
            serializeException(sf, out); 
874
        } catch(NumberFormatException ne) {
875
            response.setStatus(500);
876
            InvalidRequest ir = new InvalidRequest("1030", "Invalid format for parameter: " + ne.getMessage());
877
            serializeException(ir, out);
878
        } catch (Exception e) {
879
            e.printStackTrace();
880
            response.setStatus(500);
881
            ServiceFailure sf = new ServiceFailure("1030", 
882
                    "Exception " + e.getClass().getName() + " raised while handling listObjects request: " + 
883
                    e.getMessage());
884
            serializeException(sf, out);
885
        }
886
    }
887
    
888
    /**
889
     * parse a date and return it in GMT if it ends with a 'Z'
890
     * @param date
891
     * @return
892
     * @throws ParseException
893
     */
894
    private Date parseDateAndConvertToGMT(String date) throws ParseException
895
    {
896
        try
897
        {   //the format we want
898
            return dateFormat.parse(date);
899
        }
900
        catch(java.text.ParseException pe)
901
        {   //try another legacy format
902
            DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
903
            dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
904
            return dateFormat2.parse(date);
905
        }    
906
        
907
        /*System.out.println("Parsing date " + date);
908
        Date d = dateFormat.parse(date);
909
        
910
        if(date.endsWith("Z"))
911
        {
912
            Calendar lTime = Calendar.getInstance();
913
            lTime.setTime(d);
914
            Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
915
            zTime.set(Calendar.MONTH, lTime.get(Calendar.MONTH));
916
            zTime.set(Calendar.DATE, lTime.get(Calendar.DATE));
917
            zTime.set(Calendar.YEAR, lTime.get(Calendar.YEAR));
918
            zTime.set(Calendar.HOUR, lTime.get(Calendar.HOUR));
919
            zTime.set(Calendar.MINUTE, lTime.get(Calendar.MINUTE));
920
            zTime.set(Calendar.SECOND, lTime.get(Calendar.SECOND));
921
            
922
            System.out.println("date parsed to " + zTime.getTime());
923
            return zTime.getTime();
924
        }
925
        
926
        System.out.println("date parsed, but not converted. returned as " + d);
927
        return d;*/
928
    }
929

    
930
    /**
931
     * Implements REST version of DataONE CRUD API --> getSystemMetadata
932
     * @param guid ID of data object to be read
933
     */
934
    private void getSystemMetadataObject(String guid) {
935
        CrudService cs = CrudService.getInstance();
936
        cs.setParamsFromRequest(request);
937
        AuthToken token = new AuthToken(sessionId);
938
        OutputStream out = null;
939
        try {
940
            response.setContentType("text/xml");
941
            response.setStatus(200);
942
            out = response.getOutputStream();
943
            Identifier id = new Identifier();
944
            id.setValue(guid);
945
            SystemMetadata sysmeta = cs.getSystemMetadata(token, id);
946
            
947
            // Serialize and write it to the output stream
948
            try {
949
                //TODO: look at the efficiency of this method.  The system metadata
950
                //is read from metacat (in CrudService) as xml, then serialized
951
                //to a SystemMetadat object, then returned here, then serizlized
952
                //back to XML to be sent to the response.
953
                serializeServiceType(SystemMetadata.class, sysmeta, out);
954
            } catch (JiBXException e) {
955
                throw new ServiceFailure("1190", "Failed to serialize SystemMetadata: " + e.getMessage());
956
            }
957
        } catch (BaseException e) {
958
            response.setStatus(500);
959
                serializeException(e, out);
960
        } catch (IOException e) {
961
            response.setStatus(500);
962
            ServiceFailure sf = new ServiceFailure("1030", 
963
                    "Error in ResourceHandler.getSystemMetadataObject: " + e.getMessage());
964
            serializeException(sf, out);
965
        } finally {
966
            IOUtils.closeQuietly(out);
967
        }
968
    }
969
    
970
    /**
971
     * serialize an object of type to out
972
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
973
     * @param object the object to serialize
974
     * @param out the stream to serialize it to
975
     * @throws JiBXException
976
     */
977
    private void serializeServiceType(Class type, Object object, OutputStream out)
978
      throws JiBXException
979
    {
980
        IBindingFactory bfact = BindingDirectory.getFactory(type);
981
        IMarshallingContext mctx = bfact.createMarshallingContext();
982
        mctx.marshalDocument(object, "UTF-8", null, out);
983
    }
984
    
985
    /**
986
     * deserialize an object of type from is
987
     * @param type the class of the object to serialize (i.e. SystemMetadata.class)
988
     * @param is the stream to deserialize from
989
     * @throws JiBXException
990
     */
991
    private Object deserializeServiceType(Class type, InputStream is)
992
      throws JiBXException
993
    {
994
        IBindingFactory bfact = BindingDirectory.getFactory(type);
995
        IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
996
        Object o = (Object) uctx.unmarshalDocument(is, null);
997
        return o;
998
    }
999
    
1000
    /**
1001
     * Earthgrid API > Query Service > Query Function : translates ecogrid query document to metacat query 
1002
     * then calls DBQuery > createResultDocument function and then again translate resultset to ecogrid resultset
1003
     * 
1004
     * NOTE:
1005
     *      This is the only method that uses EcoGrid classes for its implementation.  
1006
     *      It does so because it takes an EcoGrid Query as input, and outputs an
1007
     *      EcoGrid ResultSet document.  These documents are parsed by the auto-generated
1008
     *      EcoGrid classes from axis, and so we link to them here rather than re-inventing them.
1009
     *      This creates a circular dependency, because the Metacat classes are needed
1010
     *      to build the EcoGrid implementation, and the EcoGrid jars are needed to build this query()
1011
     *      method.  This circularity could be resolved by moving the EcoGrid classes
1012
     *      to Metacat directly.  As we transition away from EcoGrid SOAP methods in
1013
     *      favor of these REST interfaces, this circular dependency can be eliminated.
1014
     *        
1015
     * @throws Exception
1016
     */
1017
    private void query() throws Exception {
1018
        /*  This block commented out because of the EcoGrid circular dependency.
1019
         *  For now, query will not be supported until the circularity can be
1020
         *  resolved, probably by moving the ecogrid query syntax transformers
1021
         *  directly into the Metacat codebase.  MBJ 2010-02-03
1022
         
1023
        try {
1024
            EcogridQueryParser parser = new EcogridQueryParser(request
1025
                    .getReader());
1026
            parser.parseXML();
1027
            QueryType queryType = parser.getEcogridQuery();
1028
            EcogridJavaToMetacatJavaQueryTransformer queryTransformer = 
1029
                new EcogridJavaToMetacatJavaQueryTransformer();
1030
            QuerySpecification metacatQuery = queryTransformer
1031
                    .transform(queryType);
1032

    
1033
            DBQuery metacat = new DBQuery();
1034

    
1035
            boolean useXMLIndex = (new Boolean(PropertyService
1036
                    .getProperty("database.usexmlindex"))).booleanValue();
1037
            String xmlquery = "query"; // we don't care the query in resultset,
1038
            // the query can be anything
1039
            PrintWriter out = null; // we don't want metacat result, so set out null
1040

    
1041
            // parameter: queryspecification, user, group, usingIndexOrNot
1042
            StringBuffer result = metacat.createResultDocument(xmlquery,
1043
                    metacatQuery, out, username, groupNames, useXMLIndex);
1044

    
1045
            // create result set transfer		
1046
            String saxparser = PropertyService.getProperty("xml.saxparser");
1047
            MetacatResultsetParser metacatResultsetParser = new MetacatResultsetParser(
1048
                    new StringReader(result.toString()), saxparser, queryType
1049
                            .getNamespace().get_value());
1050
            ResultsetType records = metacatResultsetParser.getEcogridResult();
1051

    
1052
            System.out
1053
                    .println(EcogridResultsetTransformer.toXMLString(records));
1054
            response.setContentType("text/xml");
1055
            out = response.getWriter();
1056
            out.print(EcogridResultsetTransformer.toXMLString(records));
1057

    
1058
        } catch (Exception e) {
1059
            e.printStackTrace();
1060
        }*/
1061
        response.setContentType("text/xml");
1062
        response.setStatus(501);
1063
        PrintWriter out = response.getWriter();
1064
        out.print("<error>Query operation not yet supported by Metacat.</error>");
1065
        out.close();
1066
    }
1067
    
1068
    private String streamToString(InputStream is)
1069
    throws IOException
1070
    {
1071
        return IOUtil.toString(is);
1072
    }
1073

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

    
1392
                // Check if the objectId exists
1393
                IdentifierManager im = IdentifierManager.getInstance();
1394
                if (im.identifierExists(guid)) {
1395
                    throw new IdentifierNotUnique("1000", "Identifier is already in use: " + guid);
1396
                }
1397

    
1398
                logMetacat.debug("Commence creation...");
1399
                IBindingFactory bfact =
1400
                    BindingDirectory.getFactory(SystemMetadata.class);
1401
                IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1402
                SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1403

    
1404
                CrudService cs = CrudService.getInstance();
1405
                AuthToken token = new AuthToken(sessionId); 
1406
                cs.setParamsFromRequest(request);
1407
                Identifier id = new Identifier();
1408
                id.setValue(guid);
1409
                cs.create(token, id, object, m);
1410
                
1411
            } else if (action.equals(FUNCTION_NAME_UPDATE)) { //handle updates
1412
                IdentifierManager im = IdentifierManager.getInstance();
1413
                CrudService cs = CrudService.getInstance();
1414
                Identifier obsoletedGuid = new Identifier();
1415
                Identifier id = new Identifier();
1416
                id.setValue(guid);
1417
                AuthToken token = new AuthToken(sessionId);
1418
                
1419
                //do some checks
1420
                if(params.get("obsoletedGuid") == null)
1421
                {
1422
                    throw new InvalidRequest("1202", "obsoletedGuid must be contained in the request parameters.");
1423
                }
1424
                //get the obsoletedGuid
1425
                String[] obsGuidS = params.get("obsoletedGuid");
1426
                obsoletedGuid.setValue(obsGuidS[0]);
1427
                
1428
                if (!im.identifierExists(obsoletedGuid.getValue())) 
1429
                {
1430
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1431
                }
1432
                
1433
                
1434
                logMetacat.debug("Commence update...");
1435
                
1436
                //get the systemmetadata
1437
                IBindingFactory bfact =
1438
                        BindingDirectory.getFactory(SystemMetadata.class);
1439
                    IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1440
                    SystemMetadata m = (SystemMetadata) uctx.unmarshalDocument(sysmeta, null);
1441
                
1442
                //do the update
1443
                try
1444
                {
1445
                    cs.setParamsFromRequest(request);
1446
                    Identifier rId = cs.update(token, id, object, obsoletedGuid, m);
1447
                }
1448
                catch(NotFound e)
1449
                {
1450
                    throw new InvalidRequest("1202", "The guid you are trying to update does not exist.");
1451
                }
1452
                
1453
            } else {
1454
                throw new InvalidRequest("1000", "Operation must be create or update.");
1455
            }
1456
            
1457
            //clean up the MMP files
1458
            parts.get("sysmeta").delete();
1459
            parts.get("object").delete();
1460
        } catch (NotAuthorized e) {
1461
            response.setStatus(500);
1462
            serializeException(e, out);
1463
        } catch (InvalidToken e) {
1464
            response.setStatus(500);
1465
            serializeException(e, out);
1466
        } catch (ServiceFailure e) {
1467
            response.setStatus(500);
1468
            serializeException(e, out);
1469
        } catch (IdentifierNotUnique e) {
1470
            response.setStatus(500);
1471
            serializeException(e, out);
1472
        } catch (UnsupportedType e) {
1473
            response.setStatus(500);
1474
            serializeException(e, out);
1475
        } catch (InsufficientResources e) {
1476
            response.setStatus(500);
1477
            serializeException(e, out);
1478
        } catch (InvalidSystemMetadata e) {
1479
            response.setStatus(500);
1480
            serializeException(e, out);
1481
        } catch (NotImplemented e) {
1482
            response.setStatus(500);
1483
            serializeException(e, out);
1484
        } catch (InvalidRequest e) {
1485
            response.setStatus(500);
1486
            serializeException(e, out);
1487
        } /*catch (MessagingException e) {
1488
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1489
            serializeException(sf, out);
1490
        } catch (IOException e) {
1491
            response.setStatus(500);
1492
            ServiceFailure sf = new ServiceFailure("1000", e.getMessage());
1493
            serializeException(sf, out);
1494
        }*/ catch (JiBXException e) {
1495
            response.setStatus(500);
1496
            e.printStackTrace(System.out);
1497
            InvalidSystemMetadata ism = new InvalidSystemMetadata("1080", e.getMessage());
1498
            serializeException(ism, out);
1499
        }
1500
        finally
1501
        {
1502
            if(parts != null)
1503
            {
1504
                parts.get("sysmeta").delete();
1505
                parts.get("object").delete();
1506
            }
1507
        }
1508
    }
1509

    
1510
    /**
1511
     * Earthgrid API > Put Service > Delete Function : calls MetacatHandler > handleDeleteAction  
1512
     * 
1513
     * @param guid ID of data object to be deleted
1514
     * @throws IOException
1515
     */
1516
    private void deleteObject(String guid) throws IOException 
1517
    {
1518
        // Look up the localId for this global identifier
1519
        IdentifierManager im = IdentifierManager.getInstance();
1520
        String localId = "";
1521
        try {
1522
            localId = im.getLocalId(guid);
1523
        } catch (McdbDocNotFoundException e) {
1524
            // TODO: Need to return the proper DataONE exception
1525
        }
1526
        
1527
        params.put("docid", new String[] { localId });
1528
        PrintWriter out = response.getWriter();
1529
        response.setStatus(200);
1530
        response.setContentType("text/xml");
1531
        handler.handleDeleteAction(out, params, request, response, username,
1532
                groupNames);
1533
        out.close();
1534
    }
1535
    
1536
    /**
1537
     * set the access perms on a document
1538
     * @throws IOException
1539
     */
1540
    private void setaccess() throws Exception
1541
    {
1542
        try
1543
        {
1544
            String guid = params.get("guid")[0];
1545
            Identifier id = new Identifier();
1546
            id.setValue(guid);
1547
            AuthToken token = new AuthToken(sessionId);
1548
            String principal = params.get("principal")[0];
1549
            String permission = params.get("permission")[0];
1550
            String permissionType = params.get("permissionType")[0];
1551
            String permissionOrder = params.get("permissionOrder")[0];
1552
            String setSystemMetadata = params.get("setsystemmetadata")[0];
1553
            boolean ssm = false;
1554
            if(setSystemMetadata.equals("true") || setSystemMetadata.equals("TRUE") ||
1555
                    setSystemMetadata.equals("yes"))
1556
            {
1557
                ssm = true;
1558
            }
1559
            CrudService cs = CrudService.getInstance();
1560
            //TODO: remove the setsystemmetadata param and set this so the systemmetadata always gets set
1561
            cs.setAccess(token, id, principal, permission, permissionType, permissionOrder, ssm);
1562
        }
1563
        catch(Exception e)
1564
        {
1565
            response.setStatus(500);
1566
            printError("Error setting access in ResourceHandler: " + e.getMessage(), response);
1567
            throw e;
1568
        }
1569
    }
1570

    
1571
    /**
1572
     * Earthgrid API > Authentication Service > Login Function : calls MetacatHandler > handleLoginAction
1573
     * 
1574
     * @throws IOException
1575
     */
1576
    private void login() throws IOException {
1577
        PrintWriter out = response.getWriter();
1578
        response.setStatus(200);
1579
        response.setContentType("text/xml");
1580
        handler.handleLoginAction(out, params, request, response);
1581
        out.close();
1582
    }
1583

    
1584
    /**
1585
     * Earthgrid API > Authentication Service > Logout Function : calls MetacatHandler > handleLogoutAction
1586
     * 
1587
     * @throws IOException
1588
     */
1589
    private void logout() throws IOException {
1590
        PrintWriter out = response.getWriter();
1591
        response.setStatus(200);
1592
        response.setContentType("text/xml");
1593
        handler.handleLogoutAction(out, params, request, response);
1594
        out.close();
1595
    }
1596

    
1597
    /**
1598
     * Prints xml response
1599
     * @param message Message to be displayed
1600
     * @param response Servlet response that xml message will be printed 
1601
     * */
1602
    private void printError(String message, HttpServletResponse response) {
1603
        try {
1604
            logMetacat.error("ResourceHandler: Printing error to servlet response: " + message);
1605
            PrintWriter out = response.getWriter();
1606
            response.setContentType("text/xml");
1607
            out.println("<?xml version=\"1.0\"?>");
1608
            out.println("<error>");
1609
            out.println(message);
1610
            out.println("</error>");
1611
            out.close();
1612
        } catch (IOException e) {
1613
            e.printStackTrace();
1614
        }
1615
    }
1616
    
1617
    private void serializeException(BaseException e, OutputStream out) {
1618
        // TODO: Use content negotiation to determine which return format to use
1619
        response.setContentType("text/xml");
1620
        response.setStatus(e.getCode());
1621
        
1622
        logMetacat.error("ResourceHandler: Serializing exception with code " + e.getCode() + ": " + e.getMessage());
1623
        e.printStackTrace();
1624
        
1625
        try {
1626
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
1627
        } catch (IOException e1) {
1628
            logMetacat.error("Error writing exception to stream. " 
1629
                    + e1.getMessage());
1630
        }
1631
    }
1632

    
1633
}
(2-2/3)