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: $'
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.dataone;
24

    
25
import java.io.ByteArrayOutputStream;
26
import java.io.File;
27
import java.io.FileInputStream;
28
import java.io.FileNotFoundException;
29
import java.io.FileOutputStream;
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.OutputStream;
33
import java.security.NoSuchAlgorithmException;
34
import java.sql.SQLException;
35
import java.text.DateFormat;
36
import java.util.Calendar;
37
import java.util.Date;
38
import java.util.Enumeration;
39
import java.util.Hashtable;
40
import java.util.List;
41
import java.util.TimeZone;
42
import java.util.Timer;
43
import java.util.TimerTask;
44
import java.util.Vector;
45

    
46
import javax.servlet.http.HttpServletRequest;
47

    
48
import org.apache.commons.io.IOUtils;
49
import org.apache.log4j.Logger;
50
import org.dataone.service.exceptions.BaseException;
51
import org.dataone.service.exceptions.IdentifierNotUnique;
52
import org.dataone.service.exceptions.InsufficientResources;
53
import org.dataone.service.exceptions.InvalidRequest;
54
import org.dataone.service.exceptions.InvalidSystemMetadata;
55
import org.dataone.service.exceptions.InvalidToken;
56
import org.dataone.service.exceptions.NotAuthorized;
57
import org.dataone.service.exceptions.NotFound;
58
import org.dataone.service.exceptions.NotImplemented;
59
import org.dataone.service.exceptions.ServiceFailure;
60
import org.dataone.service.exceptions.UnsupportedType;
61
import org.dataone.service.mn.MemberNodeCrud;
62
import org.dataone.service.types.AuthToken;
63
import org.dataone.service.types.Checksum;
64
import org.dataone.service.types.ChecksumAlgorithm;
65
import org.dataone.service.types.DescribeResponse;
66
import org.dataone.service.types.Event;
67
import org.dataone.service.types.Identifier;
68
import org.dataone.service.types.Log;
69
import org.dataone.service.types.LogEntry;
70
import org.dataone.service.types.NodeReference;
71
import org.dataone.service.types.ObjectFormat;
72
import org.dataone.service.types.ObjectList;
73
import org.dataone.service.types.Principal;
74
import org.dataone.service.types.SystemMetadata;
75
import org.dataone.service.types.util.ServiceTypeUtil;
76
import org.jibx.runtime.BindingDirectory;
77
import org.jibx.runtime.IBindingFactory;
78
import org.jibx.runtime.IMarshallingContext;
79
import org.jibx.runtime.IUnmarshallingContext;
80
import org.jibx.runtime.JiBXException;
81

    
82
import edu.ucsb.nceas.metacat.AccessionNumber;
83
import edu.ucsb.nceas.metacat.AccessionNumberException;
84
import edu.ucsb.nceas.metacat.DocumentImpl;
85
import edu.ucsb.nceas.metacat.EventLog;
86
import edu.ucsb.nceas.metacat.IdentifierManager;
87
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
88
import edu.ucsb.nceas.metacat.McdbException;
89
import edu.ucsb.nceas.metacat.MetacatHandler;
90
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
91
import edu.ucsb.nceas.metacat.client.rest.MetacatRestClient;
92
import edu.ucsb.nceas.metacat.properties.PropertyService;
93
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
94
import edu.ucsb.nceas.metacat.service.SessionService;
95
import edu.ucsb.nceas.metacat.util.DocumentUtil;
96
import edu.ucsb.nceas.metacat.util.SessionData;
97
import edu.ucsb.nceas.utilities.ParseLSIDException;
98
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
99

    
100
/**
101
 * 
102
 * Implements DataONE MemberNode CRUD API for Metacat. 
103
 * 
104
 * @author Matthew Jones
105
 */
106
public class CrudService implements MemberNodeCrud
107
{
108
    private static CrudService crudService = null;
109

    
110
    private MetacatHandler handler;
111
    private Hashtable<String, String[]> params;
112
    private Logger logMetacat = null;
113
    private Logger logCrud = null;
114
    
115
    private String metacatUrl;
116
    
117
    /**
118
     * singleton accessor
119
     */
120
    public static CrudService getInstance() 
121
    {
122
      if(crudService == null)
123
      {
124
        crudService = new CrudService();
125
      }
126
      
127
      return crudService;
128
    }
129
    
130
    /**
131
     * Constructor, private for singleton access
132
     */
133
    private CrudService() {
134
        logMetacat = Logger.getLogger(CrudService.class);
135
        logCrud = Logger.getLogger("DataOneLogger");
136
        try
137
        {
138
            String server = PropertyService.getProperty("server.name");
139
            String port = PropertyService.getProperty("server.httpPort");
140
            String context = PropertyService.getProperty("application.context");
141
            metacatUrl = "http://" + server + ":" + port + "/" + context + "/d1";
142
            logMetacat.debug("Initializing CrudService with url " + metacatUrl);
143
        }
144
        catch(Exception e)
145
        {
146
            logMetacat.error("Could not find servlet url in CrudService: " + e.getMessage());
147
            e.printStackTrace();
148
            throw new RuntimeException("Error getting servlet url in CrudService: " + e.getMessage());
149
        }
150
        
151
        params = new Hashtable<String, String[]>();
152
        handler = new MetacatHandler(new Timer());
153
    }
154
    
155
    /**
156
     * return the context url CrudService is using.
157
     */
158
    public String getContextUrl()
159
    {
160
        return metacatUrl;
161
    }
162
    
163
    /**
164
     * Set the context url that this service uses.  It is normally not necessary
165
     * to call this method unless you are trying to connect to a server other
166
     * than the one in which this service is installed.  Otherwise, this value is
167
     * taken from the metacat.properties file (server.name, server.port, application.context).
168
     */
169
    public void setContextUrl(String url)
170
    {
171
        metacatUrl = url;
172
    }
173
    
174
    /**
175
     * set the params for this service from an HttpServletRequest param list
176
     */
177
    public void setParamsFromRequest(HttpServletRequest request)
178
    {
179
        @SuppressWarnings("unchecked")
180
        Enumeration<String> paramlist = request.getParameterNames();
181
        while (paramlist.hasMoreElements()) {
182
            String name = (String) paramlist.nextElement();
183
            String[] value = (String[])request.getParameterValues(name);
184
            params.put(name, value);
185
        }
186
    }
187
    
188
    /**
189
     * Authenticate against metacat and get a token.
190
     * @param username
191
     * @param password
192
     * @return
193
     * @throws ServiceFailure
194
     */
195
    public AuthToken authenticate(String username, String password)
196
      throws ServiceFailure
197
    {
198
        /* TODO:
199
         * This method is not in the original D1 crud spec.  It is highly
200
         * metacat centric.  Higher level decisions need to be made on authentication
201
         * interfaces for D1 nodes.
202
         */
203
        try
204
        {
205
            MetacatRestClient restClient = new MetacatRestClient(getContextUrl());   
206
            String response = restClient.login(username, password);
207
            String sessionid = restClient.getSessionId();
208
            SessionService sessionService = SessionService.getInstance();
209
            sessionService.registerSession(new SessionData(sessionid, username, new String[0], password, "CrudServiceLogin"));
210
            AuthToken token = new AuthToken(sessionid);
211
            EventLog.getInstance().log(metacatUrl,
212
                    username, null, "authenticate");
213
            logCrud.info("authenticate");
214
            return token;
215
        }
216
        catch(Exception e)
217
        {
218
            throw new ServiceFailure("1620", "Error authenticating with metacat: " + e.getMessage());
219
        }
220
    }
221
    
222
    /**
223
     * set the parameter values needed for this request
224
     */
225
    public void setParameter(String name, String[] value)
226
    {
227
        params.put(name, value);
228
    }
229
    
230
    /**
231
     * Generate SystemMetadata for any object in the object store that does
232
     * not already have it.  SystemMetadata documents themselves, are, of course,
233
     * exempt.  This is a utility method for migration of existing object 
234
     * stores to DataONE where SystemMetadata is required for all objects.  See 
235
     * https://trac.dataone.org/ticket/591
236
     * 
237
     * @param token an authtoken with appropriate permissions to read all 
238
     * documents in the object store.  To work correctly, this should probably
239
     * be an adminstrative credential.
240
     */
241
    public void generateMissingSystemMetadata(AuthToken token) {
242
        IdentifierManager im = IdentifierManager.getInstance();
243
        //get the list of ids with no SM
244
        List<String> idList = im.getLocalIdsWithNoSystemMetadata();
245
        for (String localId : idList) { 
246
            //for each id, add a system metadata doc
247
            generateMissingSystemMetadata(token, localId);
248
        }
249
        logCrud.info("generateMissingSystemMetadata(token)");
250
    }
251
    
252
    /**
253
     * Generate SystemMetadata for a particular object with identifier localId.
254
     * This is a utility method for migration of existing objects 
255
     * to DataONE where SystemMetadata is required for all objects.
256
     * 
257
     * @param token an authtoken with appropriate permissions to read all 
258
     *        documents in the object store.  To work correctly, this should
259
     *        be an adminstrative credential.
260
     * @param localId the identifier of the object to be processed
261
     */
262
    public void generateMissingSystemMetadata(AuthToken token, String localId) {
263
        System.out.println("Creating SystemMetadata for localId " + localId);
264
        try {
265
            //generate required system metadata fields from the document
266
            SystemMetadata sm = createSystemMetadata(localId, token);
267
            
268
            //insert the systemmetadata object
269
            SessionData sessionData = getSessionData(token);
270
            String smlocalid = insertSystemMetadata(sm, sessionData);
271
            System.out.println("setting access on SM doc with localid " + smlocalid);
272
            handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid, "public", "4", "allow", "allowFirst");
273

    
274
            String username = "public";
275
            if (sessionData != null) {
276
                username = sessionData.getUserName();
277
            }
278
            EventLog.getInstance().log(metacatUrl, username, localId, "generateMissingSystemMetadata");
279
        } catch (Exception e) { // TODO: Please don't catch Exception -- it masks bad things
280
            e.printStackTrace();
281
            System.out.println("Exception generating missing system metadata: " + e.getMessage());
282
            logMetacat.error("Could not generate missing system metadata: " + e.getMessage());
283
        }
284
        logCrud.info("generateMissingSystemMetadata(token, localId)");
285
    }
286
    
287
    /**
288
     * create an object via the crud interface
289
     */
290
    public Identifier create(AuthToken token, Identifier guid, 
291
            InputStream object, SystemMetadata sysmeta) throws InvalidToken, 
292
            ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType, 
293
            InsufficientResources, InvalidSystemMetadata, NotImplemented {
294
        logMetacat.debug("Starting CrudService.create()...");
295
        
296
        // authenticate & get user info
297
        SessionData sessionData = getSessionData(token);
298
        String username = "public";
299
        String[] groups = null;
300
        if(sessionData != null)
301
        {
302
            username = sessionData.getUserName();
303
            groups = sessionData.getGroupNames();
304
        }
305
        String localId = null;
306

    
307
        if (username == null || username.equals("public"))
308
        {
309
            //TODO: many of the thrown exceptions do not use the correct error codes
310
            //check these against the docs and correct them
311
            throw new NotAuthorized("1100", "User " + username + " is not authorized to create content." +
312
                    "  If you are not logged in, please do so and retry the request.");
313
        }
314
        
315
        // verify that guid == SystemMetadata.getIdentifier()
316
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
317
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
318
            throw new InvalidSystemMetadata("1180", 
319
                "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
320
                sysmeta.getIdentifier().getValue() + ").");
321
        }
322

    
323
        logMetacat.debug("Checking if identifier exists...");
324
        // Check that the identifier does not already exist
325
        IdentifierManager im = IdentifierManager.getInstance();
326
        if (im.identifierExists(guid.getValue())) {
327
            throw new IdentifierNotUnique("1120", 
328
                "GUID is already in use by an existing object.");
329
        }
330

    
331
        // Check if we are handling metadata or data
332
        boolean isScienceMetadata = isScienceMetadata(sysmeta);
333
        
334
        if (isScienceMetadata) {
335
            // CASE METADATA:
336
            try {
337
                //System.out.println("CrudService: inserting document with guid " + guid.getValue());
338
                this.insertDocument(object, guid, sessionData);
339
                localId = im.getLocalId(guid.getValue());
340
            } catch (IOException e) {
341
                String msg = "Could not create string from XML stream: " +
342
                    " " + e.getMessage();
343
                logMetacat.debug(msg);
344
                throw new ServiceFailure("1190", msg);
345
            } catch(Exception e) {
346
                String msg = "Unexpected error in CrudService.create: " + e.getMessage();
347
                logMetacat.debug(msg);
348
                throw new ServiceFailure("1190", msg);
349
            }
350
            
351

    
352
        } else {
353
            // DEFAULT CASE: DATA (needs to be checked and completed)
354
            localId = insertDataObject(object, guid, sessionData);
355
            
356
        }
357

    
358
        // For Metadata and Data, insert the system metadata into the object store too
359
        String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
360
        //get the document info.  add any access params for the sysmeta too
361
        //System.out.println("looking for access records to add for system " +
362
        //    "metadata who's parent doc's  local id is " + localId);
363
        try
364
        {
365
            Hashtable<String, Object> h = im.getDocumentInfo(localId.substring(0, localId.lastIndexOf(".")));
366
            Vector v = (Vector)h.get("access");
367
            for(int i=0; i<v.size(); i++)
368
            {
369
                @SuppressWarnings("unchecked")
370
                Hashtable<String, String> ah = (Hashtable<String, String>)v.elementAt(i);
371
                String principal = (String)ah.get("principal_name");
372
                String permission = (String)ah.get("permission");
373
                String permissionType = (String)ah.get("permission_type");
374
                String permissionOrder = (String)ah.get("permission_order");
375
                int perm = new Integer(permission).intValue();
376
                //System.out.println("found access record for principal " + principal);
377
                //System.out.println("permission: " + perm + " perm_type: " + permissionType + 
378
                //    " perm_order: " + permissionOrder);
379
                this.setAccess(token, guid, principal, perm, permissionType, permissionOrder, true);
380
            }
381
        }
382
        catch(Exception e)
383
        {
384
            logMetacat.error("Error setting permissions on System Metadata object " + 
385
                    " with id " + sysMetaLocalId + ": " + e.getMessage());
386
            //TODO: decide if this error should cancel the entire create or
387
            //if it should continue with just a logged error.
388
        }
389
        
390
        
391
        logMetacat.debug("Returning from CrudService.create()");
392
        EventLog.getInstance().log(metacatUrl,
393
                username, localId, "create");
394
        logCrud.info("create D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
395
                ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
396
        return guid;
397
    }
398
    
399
    /**
400
     * update an existing object with a new object.  Change the system metadata
401
     * to reflect the changes and update it as well.
402
     */
403
    public Identifier update(AuthToken token, Identifier guid, 
404
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta) 
405
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
406
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
407
            NotImplemented {
408
        try
409
        {
410
            SessionData sessionData = getSessionData(token);
411
            
412
            //find the old systemmetadata (sm.old) document id (the one linked to obsoletedGuid)
413
            SystemMetadata sm = getSystemMetadata(token, obsoletedGuid);
414
            //change sm.old's obsoletedBy field 
415
            List<Identifier> l = sm.getObsoletedByList();
416
            l.add(guid);
417
            sm.setObsoletedByList(l);
418
            //update sm.old
419
            updateSystemMetadata(sm, sessionData);
420
            
421
            //change the obsoletes field of the new systemMetadata (sm.new) to point to the id of the old one
422
            sysmeta.addObsolete(obsoletedGuid);
423
            //insert sm.new
424
            String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
425
            String localId;
426
            
427
            boolean isScienceMetadata = isScienceMetadata(sysmeta);
428
            if(isScienceMetadata)
429
            {
430
                //update the doc
431
                localId = updateDocument(object, obsoletedGuid, guid, sessionData, false);
432
            }
433
            else
434
            {
435
                //update a data file, not xml
436
                localId = insertDataObject(object, guid, sessionData);
437
            }
438
            
439
            IdentifierManager im = IdentifierManager.getInstance();
440
            String username = "public";
441
            if(sessionData != null)
442
            {
443
                username = sessionData.getUserName();
444
            }
445
            EventLog.getInstance().log(metacatUrl,
446
                    username, im.getLocalId(guid.getValue()), "update");
447
            logCrud.info("update D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
448
                    ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
449
            return guid;
450
        }
451
        catch(Exception e)
452
        {
453
            throw new ServiceFailure("1310", "Error updating document in CrudService: " + e.getMessage());
454
        }
455
    }
456
    
457
    /**
458
     * set access permissions on both the science metadata and system metadata
459
     */
460
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
461
            String permissionType, String permissionOrder)
462
      throws ServiceFailure
463
    {
464
        setAccess(token, id, principal, permission, permissionType, permissionOrder, true);
465
    }
466
    
467
    /**
468
     * set access control on the doc
469
     * @param token
470
     * @param id
471
     * @param principal
472
     * @param permission
473
     */
474
    public void setAccess(AuthToken token, Identifier id, String principal, int permission,
475
      String permissionType, String permissionOrder, boolean setSystemMetadata)
476
      throws ServiceFailure
477
    {
478
        String perm = "";
479
        if(permission >= 4)
480
        {
481
            perm = "read";
482
        }
483
        if(permission >= 6)
484
        {
485
            perm = "write";
486
        }
487
        //System.out.println("perm in setAccess: " + perm);
488
        //System.out.println("permission in setAccess: " + permission);
489
        setAccess(token, id, principal, perm, permissionType, permissionOrder,
490
                setSystemMetadata);
491
       
492
    }
493
    
494
    /**
495
     * set the permission on the document
496
     * @param token
497
     * @param principal
498
     * @param permission
499
     * @param permissionType
500
     * @param permissionOrder
501
     * @return
502
     */
503
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
504
            String permissionType, String permissionOrder, boolean setSystemMetadata)
505
      throws ServiceFailure
506
    {
507
        /* TODO:
508
         * This is also not part of the D1 Crud spec.  This method is needed for
509
         * systems such as metacat where access to objects is controlled by
510
         * and ACL.  Higher level decisions need to be made about how this
511
         * should work within D1.
512
         */
513
        try
514
        {
515
            final SessionData sessionData = getSessionData(token);
516
            if(sessionData == null)
517
            {
518
                throw new ServiceFailure("1000", "User must be logged in to set access.");
519
            }
520
            IdentifierManager im = IdentifierManager.getInstance();
521
            String docid = im.getLocalId(id.getValue());
522
        
523
            String permNum = "0";
524
            if(permission.equals("read"))
525
            {
526
                permNum = "4";
527
            }
528
            else if(permission.equals("write"))
529
            {
530
                permNum = "6";
531
            }
532
            System.out.println("user " + sessionData.getUserName() + 
533
                    " is setting access level " + permNum + " for permission " + 
534
                    permissionType + " on doc with localid " + docid);
535
            handler.setAccess(metacatUrl, sessionData.getUserName(), docid, 
536
                    principal, permNum, permissionType, permissionOrder);
537
            if(setSystemMetadata)
538
            {
539
                //set the same perms on the system metadata doc
540
                String smlocalid = im.getSystemMetadataLocalId(id.getValue());
541
                System.out.println("setting access on SM doc with localid " + smlocalid);
542
                //cs.setAccess(token, smid, principal, permission, permissionType, permissionOrder);
543
                handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid,
544
                        principal, permNum, permissionType, permissionOrder);
545
            }
546
            String username = "public";
547
            if(sessionData != null)
548
            {
549
                username = sessionData.getUserName();
550
            }
551
            EventLog.getInstance().log(metacatUrl,
552
                    username, im.getLocalId(id.getValue()), "setAccess");
553
            logCrud.info("setAccess");
554
        }
555
        catch(Exception e)
556
        {
557
            e.printStackTrace();
558
            throw new ServiceFailure("1000", "Could not set access on the document with id " + id.getValue());
559
        }
560
    }
561
    
562
    /**
563
     *  Retrieve the list of objects present on the MN that match the calling 
564
     *  parameters. This method is required to support the process of Member 
565
     *  Node synchronization. At a minimum, this method should be able to 
566
     *  return a list of objects that match:
567
     *  startTime <= SystemMetadata.dateSysMetadataModified
568
     *  but is expected to also support date range (by also specifying endTime), 
569
     *  and should also support slicing of the matching set of records by 
570
     *  indicating the starting index of the response (where 0 is the index 
571
     *  of the first item) and the count of elements to be returned.
572
     *  
573
     *  If startTime or endTime is null, the query is not restricted by that parameter.
574
     *  
575
     * @see http://mule1.dataone.org/ArchitectureDocs/mn_api_replication.html#MN_replication.listObjects
576
     * @param token
577
     * @param startTime
578
     * @param endTime
579
     * @param objectFormat
580
     * @param replicaStatus
581
     * @param start
582
     * @param count
583
     * @return ObjectList
584
     * @throws NotAuthorized
585
     * @throws InvalidRequest
586
     * @throws NotImplemented
587
     * @throws ServiceFailure
588
     * @throws InvalidToken
589
     */
590
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
591
        ObjectFormat objectFormat, boolean replicaStatus, int start, int count)
592
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
593
    {
594
      //ObjectList ol = new ObjectList();
595
      //final SessionData sessionData = getSessionData(token);
596
      //int totalAfterQuery = 0;
597
      
598
      
599
      return IdentifierManager.getInstance().querySystemMetadata(startTime, endTime,
600
              objectFormat, replicaStatus, start, count);
601
      
602
      
603
      /////////////////////////////////////////////////////////////////////////
604
      ///////////////// OLD CODE //////////////////////////////////////////////
605
      /////////////////////////////////////////////////////////////////////////
606
      
607
//      try
608
//      {
609
//          if (PropertyService.getProperty("database.queryCacheOn").equals("true"))
610
//          {
611
//              //System.out.println("the string stored into cache is "+ resultsetBuffer.toString());
612
//              DBQuery.clearQueryResultCache();
613
//          }
614
//      }
615
//      catch (PropertyNotFoundException e1)
616
//      {
617
//          //just don't do anything
618
//      }
619
//      
620
//      try
621
//      {
622
//          //TODO: Milliseconds need to be added to the dateFormat
623
//          System.out.println("=========== Listing Objects =============");
624
//          System.out.println("Current server time is: " + new Date());
625
//          if(startTime != null)
626
//          {
627
//              System.out.println("query start time is " + startTime);
628
//          }
629
//          if(endTime != null)
630
//          {
631
//              System.out.println("query end time is " + endTime);
632
//          }
633
//          params.clear();
634
//          params.put("qformat", new String[] {PropertyService.getProperty("crudService.listObjects.QFormat")});
635
//          params.put("action", new String[] {"squery"});
636
//          params.put("query", new String[] {createListObjectsPathQueryDocument()});
637
//          
638
//          /*System.out.println("query is: metacatUrl: " + metacatUrl + " user: " + sessionData.getUserName() +
639
//                  " sessionid: " + sessionData.getId() + " params: ");
640
//          String url = metacatUrl + "/metacat?action=query&sessionid=" + sessionData.getId();
641
//          Enumeration keys = params.keys();
642
//          while(keys.hasMoreElements())
643
//          {
644
//              String key = (String)keys.nextElement();
645
//              String[] parr = params.get(key);
646
//              for(int i=0; i<parr.length; i++)
647
//              {
648
//                  System.out.println("param " + key + ": " + parr[i]);
649
//                  url += "&" + key + "=" + parr[i] ;
650
//              }
651
//          }
652
//          System.out.println("query url: " + url);
653
//          */
654
//          String username = "public";
655
//          String[] groups = null;
656
//          String sessionid = "";
657
//          if(sessionData != null)
658
//          {
659
//              username = sessionData.getUserName();
660
//              groups = sessionData.getGroupNames();
661
//              sessionid = sessionData.getId();
662
//          }
663
//          
664
//          MetacatResultSet rs = handler.query(metacatUrl, params, username, 
665
//                  groups, sessionid);
666
//          List docs = rs.getDocuments();
667
//          
668
//          System.out.println("query returned " + docs.size() + " documents.");
669
//          Vector<Document> docCopy = new Vector<Document>();
670
//          
671
//          //preparse the list to remove any that don't match the query params
672
//          /* TODO: this type of query/subquery processing is probably not scalable
673
//           * to larger object stores.  This code should be revisited.  The metacat
674
//           * query handler should probably be altered to handle the type of query 
675
//           * done here.
676
//           */ 
677
//          for(int i=0; i<docs.size(); i++)
678
//          {
679
//              Document d = (Document)docs.get(i);
680
//              
681
//              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
682
//              
683
//              if(returnedObjectFormat != null && 
684
//                 objectFormat != null && 
685
//                 !objectFormat.toString().trim().equals(returnedObjectFormat.toString().trim()))
686
//              { //make sure the objectFormat is the one specified
687
//                  continue;
688
//              }
689
//              
690
//              String dateSMM = d.getField("dateSysMetadataModified");
691
//              if((startTime != null || endTime != null) && dateSMM == null)
692
//              {  //if startTime or endTime are not null, we need a date to compare to
693
//                  continue;
694
//              }
695
//              
696
//              //date parse
697
//              Date dateSysMetadataModified = null;
698
//              if(dateSMM != null)
699
//              {
700
//
701
//                  /*                  
702
//                  if(dateSMM.indexOf(".") != -1)
703
//                  {  //strip the milliseconds
704
//                      //TODO: don't do this. we need milliseconds now.
705
//                      //TODO: explore ISO 8601 to figure out milliseconds
706
//                      dateSMM = dateSMM.substring(0, dateSMM.indexOf(".")) + 'Z';
707
//                  }
708
//                  */
709
//                  //System.out.println("dateSMM: " + dateSMM);
710
//                  //dateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
711
//                  try
712
//                  {   //the format we want
713
//                      dateSysMetadataModified = dateFormat.parse(dateSMM);
714
//                  }
715
//                  catch(java.text.ParseException pe)
716
//                  {   //try another legacy format
717
//                      try
718
//                      {
719
//                          DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.S'Z'");
720
//                          dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
721
//                          dateSysMetadataModified = dateFormat2.parse(dateSMM);
722
//                      }
723
//                      catch(java.text.ParseException pe2)
724
//                      {
725
//                          //try another legacy format
726
//                          DateFormat dateFormat3 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
727
//                          dateFormat3.setTimeZone(TimeZone.getTimeZone("GMT-0"));
728
//                          dateSysMetadataModified = dateFormat3.parse(dateSMM);
729
//                      }
730
//                      
731
//                  }                  
732
//              }
733
//              
734
//              /*System.out.println("====================================");
735
//              System.out.println("doc number " + i);
736
//              System.out.println("docid: " + d.docid);
737
//              System.out.println("guid: " + d.getField("identifier").trim());
738
//              System.out.println("dateSMM: " + dateSMM);
739
//              System.out.println("dateSysMetadataModified: " + dateSysMetadataModified);
740
//              System.out.println("startTime: " + startTime);
741
//              System.out.println("endtime: " + endTime);*/
742
//              
743
//              int startDateComparison = 0;
744
//              int endDateComparison = 0;
745
//              if(startTime != null)
746
//              {
747
//                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
748
//                  zTime.setTime(startTime);
749
//                  startTime = zTime.getTime();
750
//                  
751
//                  if(dateSysMetadataModified == null)
752
//                  {
753
//                      startDateComparison = -1;
754
//                  }
755
//                  else
756
//                  {
757
//                      startDateComparison = dateSysMetadataModified.compareTo(startTime);
758
//                  }
759
//                  //System.out.println("startDateCom: " + startDateComparison);
760
//              }
761
//              else
762
//              {
763
//                  startDateComparison = 1;
764
//              }
765
//              
766
//              if(endTime != null)
767
//              {
768
//                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
769
//                  zTime.setTime(endTime);
770
//                  endTime = zTime.getTime();
771
//                  
772
//                  if(dateSysMetadataModified == null)
773
//                  {
774
//                      endDateComparison = 1;
775
//                  }
776
//                  else
777
//                  {
778
//                      endDateComparison = dateSysMetadataModified.compareTo(endTime);
779
//                  }
780
//                  //System.out.println("endDateCom: " + endDateComparison);
781
//              }
782
//              else
783
//              {
784
//                  endDateComparison = -1;
785
//              }
786
//              
787
//              
788
//              if(startDateComparison < 0 || endDateComparison > 0)
789
//              { 
790
//                  continue;                  
791
//              }
792
//              
793
//              docCopy.add((Document)docs.get(i));
794
//          } //end pre-parse
795
//          
796
//          docs = docCopy;
797
//          totalAfterQuery = docs.size();
798
//          //System.out.println("total after subquery: " + totalAfterQuery);
799
//          
800
//          //make sure we don't run over the end
801
//          int end = start + count;
802
//          if(end > docs.size())
803
//          {
804
//              end = docs.size();
805
//          }
806
//          
807
//          for(int i=start; i<end; i++)
808
//          {
809
//              //get the document from the result
810
//              Document d = (Document)docs.get(i);
811
//              //System.out.println("processing doc " + d.docid);
812
//              
813
//              String dateSMM = d.getField("dateSysMetadataModified");
814
//              //System.out.println("dateSMM: " + dateSMM);
815
//              //System.out.println("parsed date: " + parseDate(dateSMM));
816
//              Date dateSysMetadataModified = null;
817
//              if(dateSMM != null)
818
//              {
819
//                  try
820
//                  {
821
//                      dateSysMetadataModified = parseDate(dateSMM);
822
//                  }
823
//                  catch(Exception e)
824
//                  { //if we fail to parse the date, just ignore the value
825
//                      dateSysMetadataModified = null;
826
//                  }
827
//              }
828
//              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
829
//                
830
//              
831
//              ObjectInfo info = new ObjectInfo();
832
//              //add the fields to the info object
833
//              Checksum cs = new Checksum();
834
//              cs.setValue(d.getField("checksum"));
835
//              String csalg = d.getField("algorithm");
836
//              if(csalg == null)
837
//              {
838
//                  csalg = "MD5";
839
//              }
840
//              ChecksumAlgorithm ca = ChecksumAlgorithm.convert(csalg);
841
//              cs.setAlgorithm(ca);
842
//              info.setChecksum(cs);
843
//              info.setDateSysMetadataModified(dateSysMetadataModified);
844
//              Identifier id = new Identifier();
845
//              id.setValue(d.getField("identifier").trim());
846
//              info.setIdentifier(id);
847
//              info.setObjectFormat(returnedObjectFormat);
848
//              String size = d.getField("size");
849
//              if(size != null)
850
//              {
851
//                  info.setSize(new Long(size.trim()).longValue());
852
//              }
853
//              //add the ObjectInfo to the ObjectList
854
//              //logCrud.info("objectFormat: " + info.getObjectFormat().toString());
855
//              //logCrud.info("id: " + info.getIdentifier().getValue());
856
//              
857
//              if(info.getIdentifier().getValue() != null)
858
//              { //id can be null from tests.  should not happen in production.
859
//                  if((info.getObjectFormat() != null && !info.getObjectFormat().toString().trim().equals("")))
860
//                  { //objectFormat needs to not be null and not be an empty string
861
//                    ol.addObjectInfo(info);
862
//                  }
863
//                  else
864
//                  {
865
//                      logCrud.info("Not adding object with null objectFormat" + info.getIdentifier().getValue().toString());
866
//                  }
867
//              }
868
//             
869
//          }
870
//      }
871
//      catch(Exception e)
872
//      {
873
//          e.printStackTrace();
874
//          logCrud.error("Error creating ObjectList: " + e.getMessage() + " cause: " + e.getCause());
875
//          throw new ServiceFailure("1580", "Error retrieving ObjectList: " + e.getMessage());
876
//      }
877
//      String username = "public";
878
//      if(sessionData != null)
879
//      {
880
//          username = sessionData.getUserName();
881
//      }
882
//      EventLog.getInstance().log(metacatUrl,
883
//              username, null, "read");
884
//      logCrud.info("listObjects");
885
//      if(totalAfterQuery < count)
886
//      {
887
//          count = totalAfterQuery;
888
//      }
889
//      ol.setCount(count);
890
//      ol.setStart(start);
891
//      ol.setTotal(totalAfterQuery);
892
//      return ol;
893
    }
894
    
895
    /**
896
     * Call listObjects with the default values for replicaStatus (true), start (0),
897
     * and count (1000).
898
     * @param token
899
     * @param startTime
900
     * @param endTime
901
     * @param objectFormat
902
     * @return
903
     * @throws NotAuthorized
904
     * @throws InvalidRequest
905
     * @throws NotImplemented
906
     * @throws ServiceFailure
907
     * @throws InvalidToken
908
     */
909
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
910
        ObjectFormat objectFormat)
911
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
912
    {
913
       return listObjects(token, startTime, endTime, objectFormat, true, 0, 1000);
914
    }
915

    
916
    /**
917
     * Delete a document. 
918
     */
919
    public Identifier delete(AuthToken token, Identifier guid)
920
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
921
            NotImplemented, InvalidRequest {
922
        logCrud.info("delete");
923
        
924
        if(token == null || token.getToken().equals("publid"))
925
        {
926
            throw new NotAuthorized("1320", "You must be logged in to delete records.");
927
        }
928
        
929
        if(guid == null || guid.getValue().trim().equals(""))
930
        {
931
            throw new InvalidRequest("1322", "No GUID specified in CrudService.delete()");
932
        }
933
        final SessionData sessionData = getSessionData(token);
934
        IdentifierManager manager = IdentifierManager.getInstance();
935
        
936
        String docid;
937
        try
938
        {
939
            docid = manager.getLocalId(guid.getValue());
940
        }
941
        catch(McdbDocNotFoundException mnfe)
942
        {
943
            throw new InvalidRequest("1322", "GUID " + guid + " not found.");
944
        }
945
        
946
        try
947
        {
948
            DocumentImpl.delete(docid, sessionData.getUserName(), sessionData.getGroupNames(), null);
949
        }
950
        catch(Exception e)
951
        {
952
            throw new ServiceFailure("1350", "Could not delete document: " + e.getMessage());
953
        }
954
        
955
        return guid;
956
    }
957

    
958
    /**
959
     * describe a document.  
960
     */
961
    public DescribeResponse describe(AuthToken token, Identifier guid)
962
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
963
            NotImplemented, InvalidRequest {
964
        logCrud.info("describe");
965
        
966
        if(token == null)
967
        {
968
            throw new InvalidToken("1370", "Authentication token is null");
969
        }
970
        
971
        if(guid == null || guid.getValue().trim().equals(""))
972
        {
973
            throw new InvalidRequest("1362", "Guid is null.  A valid guid is required.");
974
        }
975
        
976
        SystemMetadata sm = getSystemMetadata(token, guid);
977
        DescribeResponse dr = new DescribeResponse(sm.getObjectFormat(), 
978
                sm.getSize(), sm.getDateSysMetadataModified(), sm.getChecksum());
979
        return dr;
980
    }
981
    
982
    /**
983
     * get a document with a specified guid.
984
     */
985
    public InputStream get(AuthToken token, Identifier guid)
986
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
987
            NotImplemented {
988
        
989
        // Retrieve the session information from the AuthToken
990
        // If the session is expired, then the user is 'public'
991
        if(token == null)
992
        {
993
            token = new AuthToken("Public");
994
        }
995
        final SessionData sessionData = getSessionData(token);
996
        
997
        // Look up the localId for this global identifier
998
        IdentifierManager im = IdentifierManager.getInstance();
999
        
1000
        try 
1001
        {
1002
            final String localId = im.getLocalId(guid.getValue());
1003
            InputStream objectStream;
1004
            try 
1005
            {
1006
                String username = "public";
1007
                String[] groups = new String[0];
1008
                if(sessionData != null)
1009
                {
1010
                    username = sessionData.getUserName();
1011
                    groups = sessionData.getGroupNames();
1012
                }
1013
                
1014
                objectStream = readFromMetacat(localId, username, groups);
1015
                
1016
            } catch (PropertyNotFoundException e) {
1017
                e.printStackTrace();
1018
                throw new ServiceFailure("1030", "Error getting property from metacat: " + e.getMessage());
1019
            } catch (ClassNotFoundException e) {
1020
                e.printStackTrace();
1021
                throw new ServiceFailure("1030", "Class not found error when reading from metacat: " + e.getMessage());
1022
            } catch (IOException e) {
1023
                e.printStackTrace();
1024
                throw new ServiceFailure("1030", "IOException while reading from metacat: " + e.getMessage());
1025
            } catch (SQLException e) {
1026
                e.printStackTrace();
1027
                throw new ServiceFailure("1030", "SQLException while reading from metacat: " + e.getMessage());
1028
            } catch (McdbException e) {
1029
                e.printStackTrace();
1030
                throw new ServiceFailure("1030", "Metacat DB exception while reading from metacat: " + e.getMessage());
1031
            } catch (ParseLSIDException e) {
1032
                e.printStackTrace();
1033
                throw new NotFound("1020", "LSID parsing exception while reading from metacat: " + e.getMessage());
1034
            } catch (InsufficientKarmaException e) {
1035
                e.printStackTrace();
1036
                throw new NotAuthorized("1000", "User not authorized for get(): " + e.getMessage());
1037
            }
1038
        
1039
        
1040
            String username = "public";
1041
            if(sessionData != null)
1042
            {
1043
                username = sessionData.getUserName();
1044
            }
1045
            
1046
            EventLog.getInstance().log(metacatUrl,
1047
                    username, im.getLocalId(guid.getValue()), "read");
1048
            logCrud.info("get D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
1049
                    ":");
1050
            
1051
            return objectStream;
1052
        } 
1053
        catch (McdbDocNotFoundException e) 
1054
        {
1055
            throw new NotFound("1020", e.getMessage());
1056
        } 
1057
    }
1058

    
1059
    /**
1060
     * get the checksum for a document.  defaults to MD5.
1061
     */
1062
    public Checksum getChecksum(AuthToken token, Identifier guid)
1063
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1064
            InvalidRequest, NotImplemented 
1065
    {
1066
        logCrud.info("getChecksum");
1067
        return getChecksum(token, guid, "MD5");
1068
    }
1069

    
1070
    /**
1071
     * get the checksum for a document with the given algorithm
1072
     */
1073
    public Checksum getChecksum(AuthToken token, Identifier guid, 
1074
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
1075
            NotAuthorized, NotFound, InvalidRequest, NotImplemented 
1076
    {
1077
        logCrud.info("getChecksum");
1078
        SystemMetadata sm = getSystemMetadata(token, guid);
1079
        Checksum cs = sm.getChecksum();
1080
        if(cs.getAlgorithm().toString().equals(checksumAlgorithm))
1081
        {
1082
            return cs;
1083
        }
1084
        else
1085
        {
1086
            if(checksumAlgorithm == null)
1087
            {
1088
                checksumAlgorithm = "MD5";
1089
            }
1090
            InputStream docStream = get(token, guid);
1091
            String checksum;
1092
            try
1093
            {
1094
                checksum = checksum(docStream, checksumAlgorithm);
1095
            }
1096
            catch(Exception e)
1097
            {
1098
                throw new ServiceFailure("1410", "Error getting checksum: " + e.getMessage());
1099
            }
1100
            Checksum c = new Checksum();
1101
            c.setAlgorithm(ChecksumAlgorithm.convert(checksumAlgorithm));
1102
            c.setValue(checksum);
1103
            return c;
1104
        }
1105
    }
1106

    
1107
    /**
1108
     * get log records.  
1109
     */
1110
    public Log getLogRecords(AuthToken token, Date fromDate, Date toDate, Event event)
1111
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, 
1112
            NotImplemented 
1113
    {
1114
        /*System.out.println("=================== Getting log records ===================");
1115
        System.out.println("Current server time is: " + new Date());
1116
        if(fromDate != null)
1117
        {
1118
          System.out.println("query start time is " + fromDate);
1119
        }
1120
        if(toDate != null)
1121
        {
1122
          System.out.println("query end time is " + toDate);
1123
        }*/
1124
        Log log = new Log();
1125
        Vector<LogEntry> logs = new Vector<LogEntry>();
1126
        IdentifierManager im = IdentifierManager.getInstance();
1127
        EventLog el = EventLog.getInstance();
1128
        if(fromDate == null)
1129
        {
1130
            //System.out.println("setting fromdate from null");
1131
            fromDate = new Date(1);
1132
        }
1133
        if(toDate == null)
1134
        {
1135
            //System.out.println("setting todate from null");
1136
            toDate = new Date();
1137
        }
1138
        
1139
        //System.out.println("fromDate: " + fromDate);
1140
        //System.out.println("toDate: " + toDate);
1141
        
1142
        String report = el.getReport(null, null, null, null, 
1143
                new java.sql.Timestamp(fromDate.getTime()), 
1144
                new java.sql.Timestamp(toDate.getTime()), false);
1145
        
1146
        //System.out.println("report: " + report);
1147
        
1148
        String logEntry = "<logEntry>";
1149
        String endLogEntry = "</logEntry>";
1150
        int startIndex = 0;
1151
        int foundIndex = report.indexOf(logEntry, startIndex);
1152
        while(foundIndex != -1)
1153
        {
1154
            //parse out each entry
1155
            int endEntryIndex = report.indexOf(endLogEntry, foundIndex);
1156
            String entry = report.substring(foundIndex, endEntryIndex);
1157
            //System.out.println("entry: " + entry);
1158
            startIndex = endEntryIndex + endLogEntry.length();
1159
            foundIndex = report.indexOf(logEntry, startIndex);
1160
            
1161
            String entryId = getLogEntryField("entryid", entry);
1162
            String ipAddress = getLogEntryField("ipAddress", entry);
1163
            String principal = getLogEntryField("principal", entry);
1164
            String docid = getLogEntryField("docid", entry);
1165
            String eventS = getLogEntryField("event", entry);
1166
            String dateLogged = getLogEntryField("dateLogged", entry);
1167
            
1168
            LogEntry le = new LogEntry();
1169
            
1170
            Event e = Event.convert(eventS);
1171
            if(e == null)
1172
            { //skip any events that are not Dataone Crud events
1173
                continue;
1174
            }
1175
            le.setEvent(e);
1176
            Identifier entryid = new Identifier();
1177
            entryid.setValue(entryId);
1178
            le.setEntryId(entryid);
1179
            Identifier identifier = new Identifier();
1180
            try
1181
            {
1182
                //System.out.println("converting docid '" + docid + "' to a guid.");
1183
                if(docid == null || docid.trim().equals("") || docid.trim().equals("null"))
1184
                {
1185
                    continue;
1186
                }
1187
                docid = docid.substring(0, docid.lastIndexOf("."));
1188
                identifier.setValue(im.getGUID(docid, im.getLatestRevForLocalId(docid)));
1189
            }
1190
            catch(Exception ex)
1191
            { //try to get the guid, if that doesn't work, just use the local id
1192
                //throw new ServiceFailure("1030", "Error getting guid for localId " + 
1193
                //        docid + ": " + ex.getMessage());\
1194
                
1195
                //skip it if the guid can't be found
1196
                continue;
1197
            }
1198
            
1199
            le.setIdentifier(identifier);
1200
            le.setIpAddress(ipAddress);
1201
            Calendar c = Calendar.getInstance();
1202
            String year = dateLogged.substring(0, 4);
1203
            String month = dateLogged.substring(5, 7);
1204
            String date = dateLogged.substring(8, 10);
1205
            //System.out.println("year: " + year + " month: " + month + " day: " + date);
1206
            c.set(new Integer(year).intValue(), new Integer(month).intValue(), new Integer(date).intValue());
1207
            Date logDate = c.getTime();
1208
            le.setDateLogged(logDate);
1209
            NodeReference memberNode = new NodeReference();
1210
            memberNode.setValue(ipAddress);
1211
            le.setMemberNode(memberNode);
1212
            Principal princ = new Principal();
1213
            princ.setValue(principal);
1214
            le.setPrincipal(princ);
1215
            le.setUserAgent("metacat/RESTService");
1216
            
1217
            if(event == null)
1218
            {
1219
                logs.add(le);
1220
            }
1221
            
1222
            if(event != null &&
1223
               e.toString().toLowerCase().trim().equals(event.toString().toLowerCase().trim()))
1224
            {
1225
              logs.add(le);
1226
            }
1227
        }
1228
        
1229
        log.setLogEntryList(logs);
1230
        logCrud.info("getLogRecords");
1231
        return log;
1232
    }
1233
    
1234
    /**
1235
     * parse a logEntry and get the relavent field from it
1236
     * @param fieldname
1237
     * @param entry
1238
     * @return
1239
     */
1240
    private String getLogEntryField(String fieldname, String entry)
1241
    {
1242
        String begin = "<" + fieldname + ">";
1243
        String end = "</" + fieldname + ">";
1244
        //System.out.println("looking for " + begin + " and " + end + " in entry " + entry);
1245
        String s = entry.substring(entry.indexOf(begin) + begin.length(), entry.indexOf(end));
1246
        //System.out.println("entry " + fieldname + " : " + s);
1247
        return s;
1248
    }
1249

    
1250
    /**
1251
     * get the system metadata for a document with a specified guid.
1252
     */
1253
public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
1254
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1255
            InvalidRequest, NotImplemented {
1256
        
1257
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
1258
        
1259
        // Retrieve the session information from the AuthToken
1260
        // If the session is expired, then the user is 'public'
1261
        final SessionData sessionData = getSessionData(token);
1262
                
1263
        try {
1264
            IdentifierManager im = IdentifierManager.getInstance();
1265
            final String localId = im.getSystemMetadataLocalId(guid.getValue());
1266
            InputStream objectStream;
1267
            
1268
            try {
1269
                String username = "public";
1270
                String[] groupnames = null;
1271
                if(sessionData != null)
1272
                {
1273
                    username = sessionData.getUserName();
1274
                    groupnames = sessionData.getGroupNames();
1275
                }
1276
                
1277
                objectStream = readFromMetacat(localId, username, groupnames);
1278
                
1279
            } catch (PropertyNotFoundException e) {
1280
                e.printStackTrace();
1281
                throw new ServiceFailure("1090", "Property not found while reading system metadata from metacat: " + e.getMessage());
1282
            } catch (ClassNotFoundException e) {
1283
                e.printStackTrace();
1284
                throw new ServiceFailure("1090", "Class not found while reading system metadata from metacat: " + e.getMessage());
1285
            } catch (IOException e) {
1286
                e.printStackTrace();
1287
                throw new ServiceFailure("1090", "IOException while reading system metadata from metacat: " + e.getMessage());
1288
            } catch (SQLException e) {
1289
                e.printStackTrace();
1290
                throw new ServiceFailure("1090", "SQLException while reading system metadata from metacat: " + e.getMessage());
1291
            } catch (McdbException e) {
1292
                e.printStackTrace();
1293
                throw new ServiceFailure("1090", "Metacat DB Exception while reading system metadata from metacat: " + e.getMessage());
1294
            } catch (ParseLSIDException e) {
1295
                e.printStackTrace();
1296
                throw new NotFound("1060", "Error parsing LSID while reading system metadata from metacat: " + e.getMessage());
1297
            } catch (InsufficientKarmaException e) {
1298
                e.printStackTrace();
1299
                throw new NotAuthorized("1040", "User not authorized for get() on system metadata: " + e.getMessage());
1300
            }
1301
                        
1302
            // Deserialize the xml to create a SystemMetadata object
1303
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
1304
            String username = "public";
1305
            if(sessionData != null)
1306
            {
1307
                username = sessionData.getUserName();
1308
            }
1309
            EventLog.getInstance().log(metacatUrl,
1310
                    username, im.getLocalId(guid.getValue()), "read");
1311
            logCrud.info("getsystemmetadata D1GUID:" + guid.getValue()  + 
1312
                    ":D1SYSMETADATA:"+ localId + ":");
1313
            return sysmeta;
1314
            
1315
        } catch (McdbDocNotFoundException e) {
1316
            //e.printStackTrace();
1317
            throw new NotFound("1040", e.getMessage());
1318
        }                
1319
    }
1320
    
1321
    /**
1322
     * parse the date in the systemMetadata
1323
     * @param s
1324
     * @return
1325
     * @throws Exception
1326
     */
1327
    public Date parseDate(String s)
1328
      throws Exception
1329
    {
1330
        /* TODO:
1331
         * This method should be replaced by a DateFormatter
1332
         */
1333
        Date d = null;
1334
        int tIndex = s.indexOf("T");
1335
        int zIndex = s.indexOf("Z");
1336
        if(tIndex != -1 && zIndex != -1)
1337
        { //parse a date that looks like 2010-05-18T21:12:54.362Z
1338
            //System.out.println("original date: " + s);
1339
            
1340
            String date = s.substring(0, tIndex);
1341
            String year = date.substring(0, date.indexOf("-"));
1342
            String month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
1343
            String day = date.substring(date.lastIndexOf("-") + 1, date.length());
1344
            /*System.out.println("date: " + "year: " + new Integer(year).intValue() + 
1345
                    " month: " + new Integer(month).intValue() + " day: " + 
1346
                    new Integer(day).intValue());
1347
            */
1348
            String time = s.substring(tIndex + 1, zIndex);
1349
            String hour = time.substring(0, time.indexOf(":"));
1350
            String minute = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
1351
            String seconds = "00";
1352
            String milliseconds = "00";
1353
            if(time.indexOf(".") != -1)
1354
            {
1355
                seconds = time.substring(time.lastIndexOf(":") + 1, time.indexOf("."));
1356
                milliseconds = time.substring(time.indexOf(".") + 1, time.length());
1357
            }
1358
            else
1359
            {
1360
                seconds = time.substring(time.lastIndexOf(":") + 1, time.length());
1361
            }
1362
            /*System.out.println("time: " + "hour: " + new Integer(hour).intValue() + 
1363
                    " minute: " + new Integer(minute).intValue() + " seconds: " + 
1364
                    new Integer(seconds).intValue() + " milli: " + 
1365
                    new Integer(milliseconds).intValue());*/
1366
            
1367
            //d = DateFormat.getDateTimeInstance().parse(date + " " + time);
1368
            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0")/*TimeZone.getDefault()*/);
1369
            c.set(new Integer(year).intValue(), new Integer(month).intValue() - 1, 
1370
                  new Integer(day).intValue(), new Integer(hour).intValue(), 
1371
                  new Integer(minute).intValue(), new Integer(seconds).intValue());
1372
            c.set(Calendar.MILLISECOND, new Integer(milliseconds).intValue());
1373
            d = new Date(c.getTimeInMillis());
1374
            //System.out.println("d: " + d);
1375
            return d;
1376
        }
1377
        else
1378
        {  //if it's not in the expected format, try the formatter
1379
            return DateFormat.getDateTimeInstance().parse(s);
1380
        }
1381
    }
1382

    
1383
    /*
1384
     * Look up the information on the session using the token provided in
1385
     * the AuthToken.  The Session should have all relevant user information.
1386
     * If the session has expired or is invalid, the 'public' session will
1387
     * be returned, giving the user anonymous access.
1388
     */
1389
    public static SessionData getSessionData(AuthToken token) {
1390
        SessionData sessionData = null;
1391
        String sessionId = "PUBLIC";
1392
        if (token != null) {
1393
            sessionId = token.getToken();
1394
        }
1395
        
1396
        // if the session id is registered in SessionService, get the
1397
        // SessionData for it. Otherwise, use the public session.
1398
        //System.out.println("sessionid: " + sessionId);
1399
        if (sessionId != null &&
1400
            !sessionId.toLowerCase().equals("public") &&
1401
            SessionService.getInstance().isSessionRegistered(sessionId)) 
1402
        {
1403
            sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
1404
        } else {
1405
            sessionData = SessionService.getInstance().getPublicSession();
1406
        }
1407
        
1408
        return sessionData;
1409
    }
1410

    
1411
    /** 
1412
     * Determine if a given object should be treated as an XML science metadata
1413
     * object. 
1414
     * 
1415
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
1416
     * 
1417
     * @param sysmeta the SystemMetadata describig the object
1418
     * @return true if the object should be treated as science metadata
1419
     */
1420
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
1421
        /*boolean scimeta = false;
1422
        //TODO: this should be read from a .properties file instead of being hard coded
1423
        switch (sysmeta.getObjectFormat()) {
1424
            case EML_2_1_0: scimeta = true; break;
1425
            case EML_2_0_1: scimeta = true; break;
1426
            case EML_2_0_0: scimeta = true; break;
1427
            case FGDC_STD_001_1_1999: scimeta = true; break;
1428
            case FGDC_STD_001_1998: scimeta = true; break;
1429
            case NCML_2_2: scimeta = true; break;
1430
            case DSPACE_METS_SIP_1_0: scimeta = true; break;
1431
        }
1432
        
1433
        return scimeta;*/
1434
        
1435
        return MetadataTypeRegister.isMetadataType(sysmeta.getObjectFormat());
1436
    }
1437

    
1438
    /**
1439
     * insert a data doc
1440
     * @param object
1441
     * @param guid
1442
     * @param sessionData
1443
     * @throws ServiceFailure
1444
     * @returns localId of the data object inserted
1445
     */
1446
    private String insertDataObject(InputStream object, Identifier guid, 
1447
            SessionData sessionData) throws ServiceFailure {
1448
        
1449
        String username = "public";
1450
        String[] groups = null;
1451
        if(sessionData != null)
1452
        {
1453
          username = sessionData.getUserName();
1454
          groups = sessionData.getGroupNames();
1455
        }
1456

    
1457
        // generate guid/localId pair for object
1458
        logMetacat.debug("Generating a guid/localId mapping");
1459
        IdentifierManager im = IdentifierManager.getInstance();
1460
        String localId = im.generateLocalId(guid.getValue(), 1);
1461

    
1462
        try {
1463
            logMetacat.debug("Case DATA: starting to write to disk.");
1464
            if (DocumentImpl.getDataFileLockGrant(localId)) {
1465
    
1466
                // Save the data file to disk using "localId" as the name
1467
                try {
1468
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
1469
    
1470
                    File dataDirectory = new File(datafilepath);
1471
                    dataDirectory.mkdirs();
1472
    
1473
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
1474
    
1475
                    // TODO: Check that the file size matches SystemMetadata
1476
                    //                        long size = newFile.length();
1477
                    //                        if (size == 0) {
1478
                    //                            throw new IOException("Uploaded file is 0 bytes!");
1479
                    //                        }
1480
    
1481
                    // Register the file in the database (which generates an exception
1482
                    // if the localId is not acceptable or other untoward things happen
1483
                    try {
1484
                        logMetacat.debug("Registering document...");
1485
                        DocumentImpl.registerDocument(localId, "BIN", localId,
1486
                                username, groups);
1487
                        logMetacat.debug("Registration step completed.");
1488
                    } catch (SQLException e) {
1489
                        //newFile.delete();
1490
                        logMetacat.debug("SQLE: " + e.getMessage());
1491
                        e.printStackTrace(System.out);
1492
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1493
                    } catch (AccessionNumberException e) {
1494
                        //newFile.delete();
1495
                        logMetacat.debug("ANE: " + e.getMessage());
1496
                        e.printStackTrace(System.out);
1497
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1498
                    } catch (Exception e) {
1499
                        //newFile.delete();
1500
                        logMetacat.debug("Exception: " + e.getMessage());
1501
                        e.printStackTrace(System.out);
1502
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1503
                    }
1504
    
1505
                    logMetacat.debug("Logging the creation event.");
1506
                    EventLog.getInstance().log(metacatUrl,
1507
                            username, localId, "create");
1508
    
1509
                    // Schedule replication for this data file
1510
                    logMetacat.debug("Scheduling replication.");
1511
                    ForceReplicationHandler frh = new ForceReplicationHandler(
1512
                            localId, "create", false, null);
1513
    
1514
                } catch (PropertyNotFoundException e) {
1515
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
1516
                }
1517
            }
1518
            return localId;
1519
        } catch (Exception e) {
1520
            // Could not get a lock on the document, so we can not update the file now
1521
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
1522
        }
1523
    }
1524

    
1525
    /**
1526
     * write a file to a stream
1527
     * @param dir
1528
     * @param fileName
1529
     * @param data
1530
     * @return
1531
     * @throws ServiceFailure
1532
     */
1533
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
1534
        throws ServiceFailure {
1535
        
1536
        File newFile = new File(dir, fileName);
1537
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
1538

    
1539
        try {
1540
            if (newFile.createNewFile()) {
1541
                // write data stream to desired file
1542
                OutputStream os = new FileOutputStream(newFile);
1543
                long length = IOUtils.copyLarge(data, os);
1544
                os.flush();
1545
                os.close();
1546
            } else {
1547
                logMetacat.debug("File creation failed, or file already exists.");
1548
                throw new ServiceFailure("1190", "File already exists: " + fileName);
1549
            }
1550
        } catch (FileNotFoundException e) {
1551
            logMetacat.debug("FNF: " + e.getMessage());
1552
            throw new ServiceFailure("1190", "File not found: " + fileName + " " 
1553
                    + e.getMessage());
1554
        } catch (IOException e) {
1555
            logMetacat.debug("IOE: " + e.getMessage());
1556
            throw new ServiceFailure("1190", "File was not written: " + fileName 
1557
                    + " " + e.getMessage());
1558
        }
1559

    
1560
        return newFile;
1561
    }
1562

    
1563
    /**
1564
     * insert a systemMetadata doc, return the localId of the sysmeta
1565
     */
1566
    private String insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData) 
1567
        throws ServiceFailure 
1568
    {
1569
        logMetacat.debug("Starting to insert SystemMetadata...");
1570
    
1571
        // generate guid/localId pair for sysmeta
1572
        Identifier sysMetaGuid = new Identifier();
1573
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
1574
        sysmeta.setDateSysMetadataModified(new Date());
1575
        System.out.println("****inserting new system metadata with modified date " + 
1576
                sysmeta.getDateSysMetadataModified());
1577

    
1578
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
1579
        System.out.println("sysmeta: " + xml);
1580
        String localId = insertDocument(xml, sysMetaGuid, sessionData, true);
1581
        System.out.println("sysmeta inserted with localId " + localId);
1582
        //insert the system metadata doc id into the systemmetadata table to 
1583
        //link it to the data or metadata document
1584
        IdentifierManager.getInstance().createSystemMetadataMapping(
1585
                sysmeta, sysMetaGuid.getValue());
1586
        return localId;
1587
    }
1588
    
1589
    /**
1590
     * update a systemMetadata doc
1591
     */
1592
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
1593
      throws ServiceFailure
1594
    {
1595
        try
1596
        {
1597
            String smId = IdentifierManager.getInstance().getSystemMetadataLocalId(sm.getIdentifier().getValue());
1598
            System.out.println("setting date modified to " + new Date());
1599
            sm.setDateSysMetadataModified(new Date());
1600
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
1601
            String localId = updateDocument(xml, sm.getIdentifier(), null, sessionData, true);
1602
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
1603
        }
1604
        catch(Exception e)
1605
        {
1606
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getMessage());
1607
        }
1608
    }
1609
    
1610
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
1611
        throws ServiceFailure
1612
    {
1613
        return insertDocument(xml, guid, sessionData, false);
1614
    }
1615
    
1616
    /**
1617
     * insert a document
1618
     * NOTE: this method shouldn't be used from the update or create() methods.  
1619
     * we shouldn't be putting the science metadata or data objects into memory.
1620
     */
1621
    private String insertDocument(String xml, Identifier guid, SessionData sessionData,
1622
            boolean isSystemMetadata)
1623
        throws ServiceFailure
1624
    {
1625
        return insertOrUpdateDocument(xml, guid, sessionData, "insert", isSystemMetadata);
1626
    }
1627
    
1628
    /**
1629
     * insert a document from a stream
1630
     */
1631
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
1632
      throws IOException, ServiceFailure
1633
    {
1634
        //HACK: change this eventually.  we should not be converting the stream to a string
1635
        String xml = IOUtils.toString(is);
1636
        return insertDocument(xml, guid, sessionData);
1637
    }
1638
    
1639
    /**
1640
     * update a document
1641
     * NOTE: this method shouldn't be used from the update or create() methods.  
1642
     * we shouldn't be putting the science metadata or data objects into memory.
1643
     */
1644
    private String updateDocument(String xml, Identifier obsoleteGuid, 
1645
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1646
        throws ServiceFailure
1647
    {
1648
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update", isSystemMetadata);
1649
    }
1650
    
1651
    /**
1652
     * update a document from a stream
1653
     */
1654
    private String updateDocument(InputStream is, Identifier obsoleteGuid, 
1655
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1656
      throws IOException, ServiceFailure
1657
    {
1658
        //HACK: change this eventually.  we should not be converting the stream to a string
1659
        String xml = IOUtils.toString(is);
1660
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData, isSystemMetadata);
1661
        IdentifierManager im = IdentifierManager.getInstance();
1662
        if(guid != null)
1663
        {
1664
          im.createMapping(guid.getValue(), localId);
1665
        }
1666
        return localId;
1667
    }
1668
    
1669
    /**
1670
     * insert a document, return the id of the document that was inserted
1671
     */
1672
    protected String insertOrUpdateDocument(String xml, Identifier guid, 
1673
            SessionData sessionData, String insertOrUpdate, boolean isSystemMetadata) 
1674
        throws ServiceFailure {
1675
        logMetacat.debug("Starting to insert xml document...");
1676
        IdentifierManager im = IdentifierManager.getInstance();
1677

    
1678
        // generate guid/localId pair for sysmeta
1679
        String localId = null;
1680
        if(insertOrUpdate.equals("insert"))
1681
        {
1682
            localId = im.generateLocalId(guid.getValue(), 1, isSystemMetadata);
1683
        }
1684
        else
1685
        {
1686
            //localid should already exist in the identifier table, so just find it
1687
            try
1688
            {
1689
                System.out.println("updating guid " + guid.getValue());
1690
                if(!isSystemMetadata)
1691
                {
1692
                    System.out.println("looking in identifier table for guid " + guid.getValue());
1693
                    localId = im.getLocalId(guid.getValue());
1694
                }
1695
                else
1696
                {
1697
                    System.out.println("looking in systemmetadata table for guid " + guid.getValue());
1698
                    localId = im.getSystemMetadataLocalId(guid.getValue());
1699
                }
1700
                System.out.println("localId: " + localId);
1701
                //increment the revision
1702
                String docid = localId.substring(0, localId.lastIndexOf("."));
1703
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
1704
                int rev = new Integer(revS).intValue();
1705
                rev++;
1706
                docid = docid + "." + rev;
1707
                localId = docid;
1708
                System.out.println("incremented localId: " + localId);
1709
            }
1710
            catch(McdbDocNotFoundException e)
1711
            {
1712
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
1713
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
1714
            }
1715
        }
1716
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
1717
                localId);
1718

    
1719
        String[] action = new String[1];
1720
        action[0] = insertOrUpdate;
1721
        params.put("action", action);
1722
        String[] docid = new String[1];
1723
        docid[0] = localId;
1724
        params.put("docid", docid);
1725
        String[] doctext = new String[1];
1726
        doctext[0] = xml;
1727
        logMetacat.debug(doctext[0]);
1728
        params.put("doctext", doctext);
1729
        
1730
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
1731
        // onto output stream, or alternatively, capture that and parse it to 
1732
        // generate the right exceptions
1733
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
1734
        //PrintWriter pw = new PrintWriter(output);
1735
        String username = "public";
1736
        String[] groupnames = null;
1737
        if(sessionData != null)
1738
        {
1739
            username = sessionData.getUserName();
1740
            groupnames = sessionData.getGroupNames();
1741
        }
1742
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
1743
                            null, params, username, groupnames);
1744
        if(result.indexOf("<error>") != -1)
1745
        {
1746
            throw new ServiceFailure("1000", "Error inserting or updating document: " + result);
1747
        }
1748
        //String outputS = new String(output.toByteArray());
1749
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
1750
        logMetacat.debug("Finsished inserting xml document with id " + localId);
1751
        return localId;
1752
    }
1753
    
1754
    /**
1755
     * serialize a dataone type
1756
     */
1757
//    private void serializeServiceType(Class type, Object object, OutputStream out)
1758
//        throws JiBXException
1759
//    {
1760
//        IBindingFactory bfact = BindingDirectory.getFactory(type);
1761
//        IMarshallingContext mctx = bfact.createMarshallingContext();
1762
//        mctx.marshalDocument(object, "UTF-8", null, out);
1763
//    }
1764
    
1765
    /**
1766
     * serialize a system metadata doc
1767
     * @param sysmeta
1768
     * @return
1769
     * @throws ServiceFailure
1770
     */
1771
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta) 
1772
        throws ServiceFailure {
1773
        IBindingFactory bfact;
1774
        ByteArrayOutputStream sysmetaOut = null;
1775
        try {
1776
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
1777
            IMarshallingContext mctx = bfact.createMarshallingContext();
1778
            sysmetaOut = new ByteArrayOutputStream();
1779
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
1780
        } catch (JiBXException e) {
1781
            e.printStackTrace();
1782
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
1783
        }
1784
        
1785
        return sysmetaOut;
1786
    }
1787
    
1788
    /**
1789
     * deserialize a system metadata doc
1790
     * @param xml
1791
     * @return
1792
     * @throws ServiceFailure
1793
     */
1794
    public static SystemMetadata deserializeSystemMetadata(InputStream xml) 
1795
        throws ServiceFailure {
1796
        try {
1797
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
1798
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1799
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
1800
            return sysmeta;
1801
        } catch (JiBXException e) {
1802
            e.printStackTrace();
1803
            throw new ServiceFailure("1190", "Failed to deserialize and insert SystemMetadata: " + e.getMessage());
1804
        }    
1805
    }
1806
    
1807
    /**
1808
     * read a document from metacat and return the InputStream
1809
     * 
1810
     * @param localId
1811
     * @param username
1812
     * @param groups
1813
     * @return
1814
     * @throws InsufficientKarmaException
1815
     * @throws ParseLSIDException
1816
     * @throws PropertyNotFoundException
1817
     * @throws McdbException
1818
     * @throws SQLException
1819
     * @throws ClassNotFoundException
1820
     * @throws IOException
1821
     */
1822
    private InputStream readFromMetacat(String localId, String username, String[] groups)
1823
        throws InsufficientKarmaException, ParseLSIDException,
1824
        PropertyNotFoundException, McdbException, SQLException, 
1825
        ClassNotFoundException, IOException
1826
    {
1827
        File tmpDir;
1828
        try
1829
        {
1830
            tmpDir = new File(PropertyService.getProperty("application.tempDir"));
1831
        }
1832
        catch(PropertyNotFoundException pnfe)
1833
        {
1834
            logMetacat.error("ResourceHandler.writeMMPPartstoFiles: " +
1835
                    "application.tmpDir not found.  Using /tmp instead.");
1836
            tmpDir = new File("/tmp");
1837
        }
1838
        Date d = new Date();
1839
        final File outputFile = new File(tmpDir, "metacat.output." + d.getTime());
1840
        FileOutputStream dataSink = new FileOutputStream(outputFile);
1841
        
1842
        handler.readFromMetacat(metacatUrl, null, 
1843
                dataSink, localId, "xml",
1844
                username, 
1845
                groups, true, params);
1846
        
1847
        //set a timer to clean up the temp files
1848
        Timer t = new Timer();
1849
        TimerTask tt = new TimerTask() {
1850
            @Override
1851
            public void run()
1852
            {
1853
                outputFile.delete();
1854
            }
1855
        };
1856
        t.schedule(tt, 20000); //schedule after 20 secs
1857
        
1858
        InputStream objectStream = new FileInputStream(outputFile);
1859
        return objectStream;
1860
    }
1861
    
1862
    /**
1863
     * return an MD5 checksum for the stream
1864
     * @param is
1865
     * @return
1866
     * @throws IOException 
1867
     * @throws NoSuchAlgorithmException 
1868
     */
1869
    public static String checksum(InputStream is) throws NoSuchAlgorithmException, IOException
1870
    {
1871
        return checksum(is, "MD5");
1872
    }
1873
    
1874
    /**
1875
     * produce a checksum for item using the given algorithm
1876
     * @throws IOException 
1877
     * @throws NoSuchAlgorithmException 
1878
     */
1879
    public static String checksum(InputStream is, String algorithm) throws NoSuchAlgorithmException, IOException
1880
    {        
1881
        return ServiceTypeUtil.checksum(is, ChecksumAlgorithm.convert(algorithm)).getValue();
1882
    }
1883
    
1884
    /**
1885
     * parse the metacat date which looks like 2010-06-08 (YYYY-MM-DD) into
1886
     * a proper date object
1887
     * @param date
1888
     * @return
1889
     */
1890
    private Date parseMetacatDate(String date)
1891
    {
1892
        String year = date.substring(0, 4);
1893
        String month = date.substring(5, 7);
1894
        String day = date.substring(8, 10);
1895
        Calendar c = Calendar.getInstance(TimeZone.getDefault());
1896
        c.set(new Integer(year).intValue(), 
1897
              new Integer(month).intValue(), 
1898
              new Integer(day).intValue());
1899
        System.out.println("time in parseMetacatDate: " + c.getTime());
1900
        return c.getTime();
1901
    }
1902
    
1903
    /**
1904
     * find the size (in bytes) of a stream
1905
     * @param is
1906
     * @return
1907
     * @throws IOException
1908
     */
1909
    private long sizeOfStream(InputStream is)
1910
        throws IOException
1911
    {
1912
        long size = 0;
1913
        byte[] b = new byte[1024];
1914
        int numread = is.read(b, 0, 1024);
1915
        while(numread != -1)
1916
        {
1917
            size += numread;
1918
            numread = is.read(b, 0, 1024);
1919
        }
1920
        return size;
1921
    }
1922
    
1923
    /**
1924
     * create system metadata with a specified id, doc and format
1925
     * @throws McdbDocNotFoundException 
1926
     * @throws SQLException
1927
     * @throws AccessionNumberException 
1928
     * @throws NumberFormatException 
1929
     * @throws IOException 
1930
     * @throws NoSuchAlgorithmException 
1931
     * @throws PropertyNotFoundException 
1932
     */
1933
    private SystemMetadata createSystemMetadata(String localId, AuthToken token) 
1934
        throws McdbDocNotFoundException, NumberFormatException, AccessionNumberException, 
1935
        SQLException, NoSuchAlgorithmException, IOException, PropertyNotFoundException, BaseException {
1936
        
1937
        IdentifierManager im = IdentifierManager.getInstance();
1938
        Hashtable<String, Object> docInfo = im.getDocumentInfo(localId);
1939

    
1940
        //get the document text
1941
        int rev = im.getLatestRevForLocalId(localId);
1942
        Identifier identifier = new Identifier();
1943
        try {
1944
            identifier.setValue(im.getGUID(localId, rev));
1945
        } catch (McdbDocNotFoundException mcdbe) { 
1946
            //we're creating a new SM doc for a doc that is not in the identifier table                                       
1947
            //so we need to add it to
1948
            System.out.println("No guid in the identifier table.  adding it for " + localId);
1949
            im.createMapping(localId, localId);
1950
            System.out.println("mapping created for " + localId);
1951
            AccessionNumber accNum = new AccessionNumber(localId, "NONE");
1952
            identifier.setValue(im.getGUID(accNum.getDocid(), rev));
1953
        }
1954

    
1955
        System.out.println("creating system metadata for guid " + identifier.getValue());
1956
        InputStream is = this.get(token, identifier);
1957
        SystemMetadata sm = new SystemMetadata();
1958
        
1959
        //set the id
1960
        sm.setIdentifier(identifier);
1961

    
1962
        //set the object format
1963
        String doctype = (String) docInfo.get("doctype");
1964
        ObjectFormat format = ObjectFormat.convert((String) docInfo.get("doctype"));
1965
        if (format == null) {
1966
            if (doctype.trim().equals("BIN")) {
1967
                format = ObjectFormat.OCTET_STREAM;
1968
            } else {
1969
                format = ObjectFormat.convert("text/plain");
1970
            }
1971
        }
1972
        sm.setObjectFormat(format);
1973

    
1974
        //create the checksum
1975
        String checksumS = checksum(is);
1976
        ChecksumAlgorithm ca = ChecksumAlgorithm.convert("MD5");
1977
        Checksum checksum = new Checksum();
1978
        checksum.setValue(checksumS);
1979
        checksum.setAlgorithm(ca);
1980
        sm.setChecksum(checksum);
1981

    
1982
        //set the size
1983
        is = this.get(token, identifier);
1984
        sm.setSize(sizeOfStream(is));
1985

    
1986
        //submitter
1987
        Principal p = new Principal();
1988
        p.setValue((String) docInfo.get("user_owner"));
1989
        sm.setSubmitter(p);
1990
        sm.setRightsHolder(p);
1991
        try {
1992
            Date dateCreated = parseMetacatDate((String) docInfo.get("date_created"));
1993
            sm.setDateUploaded(dateCreated);
1994
            Date dateUpdated = parseMetacatDate((String) docInfo.get("date_updated"));
1995
            sm.setDateSysMetadataModified(dateUpdated);
1996
        } catch (Exception e) {
1997
            System.out.println("POSSIBLE ERROR: couldn't parse a date: " + e.getMessage());
1998
            Date dateCreated = new Date();
1999
            sm.setDateUploaded(dateCreated);
2000
            Date dateUpdated = new Date();
2001
            sm.setDateSysMetadataModified(dateUpdated);
2002
        }
2003
        NodeReference nr = new NodeReference();
2004
        nr.setValue(PropertyService.getProperty("dataone.memberNodeId"));
2005
        sm.setOriginMemberNode(nr);
2006
        sm.setAuthoritativeMemberNode(nr);
2007
        
2008
        // TODO: Need to set describes/describedBy
2009
        
2010
        return sm;
2011
    }
2012
    
2013
    /**
2014
     * create the listObjects pathQuery document
2015
     */
2016
//    private String createListObjectsPathQueryDocument()
2017
//        throws PropertyNotFoundException
2018
//    {
2019
//        String s = "<pathquery>";
2020
//        s += "<returndoctype>" + PropertyService.getProperty("crudService.listObjects.ReturnDoctype") + "</returndoctype>";
2021
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.1") + "</returnfield>";
2022
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.2") + "</returnfield>";
2023
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.3") + "</returnfield>";
2024
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.4") + "</returnfield>";
2025
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.5") + "</returnfield>";
2026
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.6") + "</returnfield>";
2027
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.7") + "</returnfield>";
2028
//        s += "<querygroup operator=\"UNION\"><queryterm casesensitive=\"false\" searchmode=\"contains\">";
2029
//        s += "<value>%</value>"; 
2030
//        s += "<pathexpr>" + PropertyService.getProperty("crudService.listObjects.ReturnField.3") + "</pathexpr>";
2031
//        s += "</queryterm></querygroup></pathquery>";
2032
//  
2033
//        return s;
2034
//    }
2035
}
2036

    
(1-1/4)