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.FileNotFoundException;
28
import java.io.FileOutputStream;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.io.OutputStream;
32
import java.io.PrintWriter;
33
import java.io.StringBufferInputStream;
34
import java.security.MessageDigest;
35
import java.sql.SQLException;
36
import java.util.*;
37
import java.text.DateFormat;
38
import java.text.SimpleDateFormat;
39

    
40
import javax.servlet.ServletContext;
41
import javax.servlet.http.HttpServletRequest;
42
import javax.servlet.http.HttpServletResponse;
43

    
44
import org.apache.commons.io.IOUtils;
45
import org.apache.log4j.Logger;
46
import org.dataone.service.exceptions.IdentifierNotUnique;
47
import org.dataone.service.exceptions.InsufficientResources;
48
import org.dataone.service.exceptions.InvalidRequest;
49
import org.dataone.service.exceptions.InvalidSystemMetadata;
50
import org.dataone.service.exceptions.InvalidToken;
51
import org.dataone.service.exceptions.NotAuthorized;
52
import org.dataone.service.exceptions.NotFound;
53
import org.dataone.service.exceptions.NotImplemented;
54
import org.dataone.service.exceptions.ServiceFailure;
55
import org.dataone.service.exceptions.UnsupportedType;
56
import org.dataone.service.mn.MemberNodeCrud;
57
import org.dataone.service.types.*;
58
import org.jibx.runtime.BindingDirectory;
59
import org.jibx.runtime.IBindingFactory;
60
import org.jibx.runtime.IMarshallingContext;
61
import org.jibx.runtime.IUnmarshallingContext;
62
import org.jibx.runtime.JiBXException;
63

    
64
import org.dataone.service.types.Identifier;
65

    
66
import com.gc.iotools.stream.is.InputStreamFromOutputStream;
67

    
68
import edu.ucsb.nceas.metacat.AccessionNumberException;
69
import edu.ucsb.nceas.metacat.MetacatResultSet;
70
import edu.ucsb.nceas.metacat.MetacatResultSet.Document;
71
import edu.ucsb.nceas.metacat.DBQuery;
72
import edu.ucsb.nceas.metacat.DocumentImpl;
73
import edu.ucsb.nceas.metacat.EventLog;
74
import edu.ucsb.nceas.metacat.IdentifierManager;
75
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
76
import edu.ucsb.nceas.metacat.McdbException;
77
import edu.ucsb.nceas.metacat.MetacatHandler;
78
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
79
import edu.ucsb.nceas.metacat.client.rest.MetacatRestClient;
80
import edu.ucsb.nceas.metacat.properties.PropertyService;
81
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
82
import edu.ucsb.nceas.metacat.service.SessionService;
83
import edu.ucsb.nceas.metacat.util.DocumentUtil;
84
import edu.ucsb.nceas.metacat.util.SessionData;
85
import edu.ucsb.nceas.utilities.ParseLSIDException;
86
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
87

    
88
/**
89
 * 
90
 * Implements DataONE MemberNode CRUD API for Metacat. 
91
 * 
92
 * @author Matthew Jones
93
 */
94
public class CrudService implements MemberNodeCrud
95
{
96
    private static CrudService crudService = null;
97

    
98
    private MetacatHandler handler;
99
    private Hashtable<String, String[]> params;
100
    private Logger logMetacat = null;
101
    private Logger logCrud = null;
102
    
103
    private String metacatUrl;
104
    
105
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZ");
106

    
107
    /**
108
     * singleton accessor
109
     */
110
    public static CrudService getInstance()
111
    {
112
      if(crudService == null)
113
      {
114
        crudService = new CrudService();
115
      }
116
      
117
      return crudService;
118
    }
119
    
120
    /**
121
     * Initializes new instance by setting servlet context,request and response.
122
     */
123
    public CrudService() {
124
    //change crud service into a singleton.  dont pass servlet data structures here
125
        logMetacat = Logger.getLogger(CrudService.class);
126
        logCrud = Logger.getLogger("DataOneLogger");
127
        try
128
        {
129
            String server = PropertyService.getProperty("server.name");
130
            String port = PropertyService.getProperty("server.httpPort");
131
            String context = PropertyService.getProperty("application.context");
132
            metacatUrl = "http://" + server + ":" + port + "/" + context;
133
            logMetacat.debug("Initializing CrudService with url " + metacatUrl);
134
        }
135
        catch(Exception e)
136
        {
137
            logMetacat.error("Could not find servlet url in CrudService: " + e.getMessage());
138
            e.printStackTrace();
139
            throw new RuntimeException("Error getting servlet url in CrudService: " + e.getMessage());
140
        }
141
        
142
        /*this.servletContext = servletContext;
143
        this.request = request;
144
        this.response = response;*/
145
        
146
        params = new Hashtable<String, String[]>();
147

    
148
        handler = new MetacatHandler(new Timer());
149

    
150
    }
151
    
152
    /**
153
     * return the context url CrudService is using.
154
     */
155
    public String getContextUrl()
156
    {
157
        return metacatUrl;
158
    }
159
    
160
    /**
161
     * Set the context url that this service uses.  It is normally not necessary
162
     * to call this method unless you are trying to connect to a server other
163
     * than the one in which this service is installed.  Otherwise, this value is
164
     * taken from the metacat.properties file (server.name, server.port, application.context).
165
     */
166
    public void setContextUrl(String url)
167
    {
168
        metacatUrl = url;
169
    }
170
    
171
    /**
172
     * set the params for this service from an HttpServletRequest param list
173
     */
174
    public void setParamsFromRequest(HttpServletRequest request)
175
    {
176
        Enumeration paramlist = request.getParameterNames();
177
        while (paramlist.hasMoreElements()) {
178
            String name = (String) paramlist.nextElement();
179
            String[] value = (String[])request.getParameterValues(name);
180
            params.put(name, value);
181
        }
182
    }
183
    
184
    /**
185
     * Authenticate against metacat and get a token.
186
     * @param username
187
     * @param password
188
     * @return
189
     * @throws ServiceFailure
190
     */
191
    public AuthToken authenticate(String username, String password)
192
      throws ServiceFailure
193
    {
194
        /* TODO:
195
         * This method is not in the original D1 crud spec.  It is highly
196
         * metacat centric.  Higher level decisions need to be made on authentication
197
         * interfaces for D1 nodes.
198
         */
199
        try
200
        {
201
            MetacatRestClient restClient = new MetacatRestClient(getContextUrl());   
202
            String response = restClient.login(username, password);
203
            String sessionid = restClient.getSessionId();
204
            SessionService sessionService = SessionService.getInstance();
205
            sessionService.registerSession(new SessionData(sessionid, username, new String[0], password, "CrudServiceLogin"));
206
            AuthToken token = new AuthToken(sessionid);
207
            EventLog.getInstance().log(metacatUrl,
208
                    username, null, "authenticate");
209
            logCrud.info("authenticate");
210
            return token;
211
        }
212
        catch(Exception e)
213
        {
214
            throw new ServiceFailure("1000", "Error authenticating with metacat: " + e.getMessage());
215
        }
216
    }
217
    
218
    /**
219
     * set the parameter values needed for this request
220
     */
221
    public void setParameter(String name, String[] value)
222
    {
223
        params.put(name, value);
224
    }
225
    
226
    /**
227
     * Generate SystemMetadata for any object in the object store that does
228
     * not already have it.  SystemMetadata documents themselves, are, of course,
229
     * exempt.  This is a utility method for migration of existing object 
230
     * stores to DataONE where SystemMetadata is required for all objects.  See 
231
     * https://trac.dataone.org/ticket/591
232
     * 
233
     * @param token an authtoken with appropriate permissions to read all 
234
     * documents in the object store.  To work correctly, this should probably
235
     * be an adminstrative credential.
236
     */
237
    public void generateMissingSystemMetadata(AuthToken token)
238
    {
239
        IdentifierManager im = IdentifierManager.getInstance();
240
        //get the list of ids with no SM
241
        List<String> l = im.getLocalIdsWithNoSystemMetadata();
242
        for(int i=0; i<l.size(); i++)
243
        { //for each id, add a system metadata doc
244
            String localId = l.get(i);
245
            //System.out.println("Creating SystemMetadata for localId " + localId);
246
            //get the document
247
            try
248
            {
249
                //generate required system metadata fields from the document
250
                SystemMetadata sm = createSystemMetadata(localId, token);
251
                //insert the systemmetadata object
252
                SessionData sessionData = getSessionData(token);
253
                insertSystemMetadata(sm, sessionData);
254
                String username = "public";
255
                if(sessionData != null)
256
                {
257
                    username = sessionData.getUserName();
258
                }
259
                EventLog.getInstance().log(metacatUrl,
260
                        username, localId, "generateMissingSystemMetadata");
261
            }
262
            catch(Exception e)
263
            {
264
                //e.printStackTrace();
265
                System.out.println("Exception generating missing system metadata: " + e.getMessage());
266
                logMetacat.error("Could not generate missing system metadata: " + e.getMessage());
267
            }
268
        }
269
        logCrud.info("generateMissingSystemMetadata");
270
    }
271
    
272
    /**
273
     * create an object via the crud interface
274
     */
275
    public Identifier create(AuthToken token, Identifier guid, 
276
            InputStream object, SystemMetadata sysmeta) throws InvalidToken, 
277
            ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType, 
278
            InsufficientResources, InvalidSystemMetadata, NotImplemented {
279
        logMetacat.debug("Starting CrudService.create()...");
280
        
281
        // authenticate & get user info
282
        SessionData sessionData = getSessionData(token);
283
        String username = "public";
284
        String[] groups = null;
285
        if(sessionData != null)
286
        {
287
            username = sessionData.getUserName();
288
            groups = sessionData.getGroupNames();
289
        }
290
        String localId = null;
291

    
292
        if (username == null || username.equals("public"))
293
        {
294
            //TODO: many of the thrown exceptions do not use the correct error codes
295
            //check these against the docs and correct them
296
            throw new NotAuthorized("1000", "User " + username + " is not authorized to create content." +
297
                    "  If you are not logged in, please do so and retry the request.");
298
        }
299
        
300
        // verify that guid == SystemMetadata.getIdentifier()
301
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
302
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
303
            throw new InvalidSystemMetadata("1180", 
304
                "GUID in method call does not match GUID in system metadata.");
305
        }
306

    
307
        logMetacat.debug("Checking if identifier exists...");
308
        // Check that the identifier does not already exist
309
        IdentifierManager im = IdentifierManager.getInstance();
310
        if (im.identifierExists(guid.getValue())) {
311
            throw new IdentifierNotUnique("1120", 
312
                "GUID is already in use by an existing object.");
313
        }
314

    
315
        // Check if we are handling metadata or data
316
        boolean isScienceMetadata = isScienceMetadata(sysmeta);
317
        
318
        /* TODO:
319
         * For EML documents, the data url field needs to be checked here
320
         * and the describes and describedBy fields of the system metadata
321
         * need to be set with the appropriate uri.  Older EML documents
322
         * maybe have ecogrid:// uri's embedded in them.  See bug 618 
323
         * (https://trac.dataone.org/ticket/618) for more info on this.
324
         */
325
        if (isScienceMetadata) {
326
            // CASE METADATA:
327
            try {
328
                //System.out.println("CrudService: inserting document with guid " + guid.getValue());
329
                this.insertDocument(object, guid, sessionData);
330
                localId = im.getLocalId(guid.getValue());
331
            } catch (IOException e) {
332
                String msg = "Could not create string from XML stream: " +
333
                    " " + e.getMessage();
334
                logMetacat.debug(msg);
335
                throw new ServiceFailure("1190", msg);
336
            } catch(Exception e) {
337
                String msg = "Unexpected error in CrudService.create: " + e.getMessage();
338
                logMetacat.debug(msg);
339
                throw new ServiceFailure("1190", msg);
340
            }
341
            
342

    
343
        } else {
344
            // DEFAULT CASE: DATA (needs to be checked and completed)
345
            localId = insertDataObject(object, guid, sessionData);
346
            
347
        }
348

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

    
891
    /**
892
     * Delete a document.  NOT IMPLEMENTED
893
     */
894
    public Identifier delete(AuthToken token, Identifier guid)
895
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
896
            NotImplemented {
897
        logCrud.info("delete");
898
        throw new NotImplemented("1000", "This method not yet implemented.");
899
    }
900

    
901
    /**
902
     * describe a document.  NOT IMPLEMENTED
903
     */
904
    public DescribeResponse describe(AuthToken token, Identifier guid)
905
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
906
            NotImplemented {
907
        logCrud.info("describe");
908
        throw new NotImplemented("1000", "This method not yet implemented.");
909
    }
910
    
911
    /**
912
     * get a document with a specified guid.
913
     */
914
    public InputStream get(AuthToken token, Identifier guid)
915
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
916
            NotImplemented {
917
        
918
        // Retrieve the session information from the AuthToken
919
        // If the session is expired, then the user is 'public'
920
        if(token == null)
921
        {
922
            token = new AuthToken("Public");
923
        }
924
        final SessionData sessionData = getSessionData(token);
925
        
926
        // Look up the localId for this global identifier
927
        IdentifierManager im = IdentifierManager.getInstance();
928
        try {
929
            final String localId = im.getLocalId(guid.getValue());
930

    
931
            final InputStreamFromOutputStream<String> objectStream = 
932
                new InputStreamFromOutputStream<String>() {
933
                
934
                @Override
935
                public String produce(final OutputStream dataSink) throws Exception {
936

    
937
                    try {
938
                        String username = "public";
939
                        String[] groups = new String[0];
940
                        if(sessionData != null)
941
                        {
942
                            username = sessionData.getUserName();
943
                            groups = sessionData.getGroupNames();
944
                        }
945
                        /*System.out.println("metacatUrl: " + metacatUrl + 
946
                            " dataSink: " + dataSink + " localId: " + localId + 
947
                            " username: " + username + " params: " + params.toString());
948
                        */    
949
                        /* TODO:
950
                         * This multithreaded approach to getting data without
951
                         * being memory bound causes problems with exception 
952
                         * handling.  The only exception the produce method
953
                         * will return is an IOException, rendering all of the 
954
                         * catch blocks below mute.  This should probably be changed
955
                         * to use a memory mapped solution instead so that we
956
                         * can properly pass exceptions to the client.
957
                         * see https://trac.dataone.org/ticket/706
958
                         */
959
                        handler.readFromMetacat(metacatUrl, null, 
960
                                dataSink, localId, "xml",
961
                                username, 
962
                                groups, true, params);
963
                    } catch (PropertyNotFoundException e) {
964
                        e.printStackTrace();
965
                        throw new ServiceFailure("1030", "Error getting property from metacat: " + e.getMessage());
966
                    } catch (ClassNotFoundException e) {
967
                        e.printStackTrace();
968
                        throw new ServiceFailure("1030", "Class not found error when reading from metacat: " + e.getMessage());
969
                    } catch (IOException e) {
970
                        e.printStackTrace();
971
                        throw new ServiceFailure("1030", "IOException while reading from metacat: " + e.getMessage());
972
                    } catch (SQLException e) {
973
                        e.printStackTrace();
974
                        throw new ServiceFailure("1030", "SQLException while reading from metacat: " + e.getMessage());
975
                    } catch (McdbException e) {
976
                        e.printStackTrace();
977
                        throw new ServiceFailure("1030", "Metacat DB exception while reading from metacat: " + e.getMessage());
978
                    } catch (ParseLSIDException e) {
979
                        e.printStackTrace();
980
                        throw new NotFound("1020", "LSID parsing exception while reading from metacat: " + e.getMessage());
981
                    } catch (InsufficientKarmaException e) {
982
                        e.printStackTrace();
983
                        throw new NotAuthorized("1000", "User not authorized for get(): " + e.getMessage());
984
                    }
985

    
986
                    return "Completed";
987
                }
988
            };
989
            String username = "public";
990
            if(sessionData != null)
991
            {
992
                username = sessionData.getUserName();
993
            }
994
            
995
            EventLog.getInstance().log(metacatUrl,
996
                    username, im.getLocalId(guid.getValue()), "read");
997
            logCrud.info("get D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
998
                    ":");
999
            return objectStream;
1000

    
1001
        } catch (McdbDocNotFoundException e) {
1002
            throw new NotFound("1020", e.getMessage());
1003
        } 
1004
    }
1005

    
1006
    /**
1007
     * get the checksum for a document.  defaults to MD5.
1008
     */
1009
    public Checksum getChecksum(AuthToken token, Identifier guid)
1010
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1011
            InvalidRequest, NotImplemented 
1012
    {
1013
        logCrud.info("getChecksum");
1014
        return getChecksum(token, guid, "MD5");
1015
    }
1016

    
1017
    /**
1018
     * get the checksum for a document with the given algorithm
1019
     */
1020
    public Checksum getChecksum(AuthToken token, Identifier guid, 
1021
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
1022
            NotAuthorized, NotFound, InvalidRequest, NotImplemented 
1023
    {
1024
        logCrud.info("getChecksum");
1025
        InputStream docStream = get(token, guid);
1026
        String checksum;
1027
        try
1028
        {
1029
            checksum = checksum(docStream, checksumAlgorithm);
1030
        }
1031
        catch(Exception e)
1032
        {
1033
            throw new ServiceFailure("1000", "Error getting checksum: " + e.getMessage());
1034
        }
1035
        Checksum c = new Checksum();
1036
        c.setAlgorithm(ChecksumAlgorithm.convert(checksumAlgorithm));
1037
        c.setValue(checksum);
1038
        return c;
1039
    }
1040

    
1041
    /**
1042
     * get log records.  
1043
     */
1044
    public Log getLogRecords(AuthToken token, Date fromDate, Date toDate, Event event)
1045
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, 
1046
            NotImplemented 
1047
    {
1048
        System.out.println("=================== Getting log records ===================");
1049
        System.out.println("Current server time is: " + new Date());
1050
        if(fromDate != null)
1051
        {
1052
          System.out.println("query start time is " + fromDate);
1053
        }
1054
        if(toDate != null)
1055
        {
1056
          System.out.println("query end time is " + toDate);
1057
        }
1058
        Log log = new Log();
1059
        Vector<LogEntry> logs = new Vector<LogEntry>();
1060
        IdentifierManager im = IdentifierManager.getInstance();
1061
        EventLog el = EventLog.getInstance();
1062
        if(fromDate == null)
1063
        {
1064
            System.out.println("setting fromdate from null");
1065
            fromDate = new Date(1);
1066
        }
1067
        if(toDate == null)
1068
        {
1069
            System.out.println("setting todate from null");
1070
            toDate = new Date();
1071
        }
1072
        
1073
        System.out.println("fromDate: " + fromDate);
1074
        System.out.println("toDate: " + toDate);
1075
        
1076
        String report = el.getReport(null, null, null, null, 
1077
                new java.sql.Timestamp(fromDate.getTime()), 
1078
                new java.sql.Timestamp(toDate.getTime()));
1079
        
1080
        //System.out.println("report: " + report);
1081
        
1082
        String logEntry = "<logEntry>";
1083
        String endLogEntry = "</logEntry>";
1084
        int startIndex = 0;
1085
        int foundIndex = report.indexOf(logEntry, startIndex);
1086
        while(foundIndex != -1)
1087
        {
1088
            //parse out each entry
1089
            int endEntryIndex = report.indexOf(endLogEntry, foundIndex);
1090
            String entry = report.substring(foundIndex, endEntryIndex);
1091
            //System.out.println("entry: " + entry);
1092
            startIndex = endEntryIndex + endLogEntry.length();
1093
            foundIndex = report.indexOf(logEntry, startIndex);
1094
            
1095
            String entryId = getLogEntryField("entryid", entry);
1096
            String ipAddress = getLogEntryField("ipAddress", entry);
1097
            String principal = getLogEntryField("principal", entry);
1098
            String docid = getLogEntryField("docid", entry);
1099
            String eventS = getLogEntryField("event", entry);
1100
            String dateLogged = getLogEntryField("dateLogged", entry);
1101
            
1102
            LogEntry le = new LogEntry();
1103
            
1104
            Event e = Event.convert(eventS);
1105
            if(e == null)
1106
            { //skip any events that are not Dataone Crud events
1107
                continue;
1108
            }
1109
            le.setEvent(e);
1110
            Identifier entryid = new Identifier();
1111
            entryid.setValue(entryId);
1112
            le.setEntryId(entryid);
1113
            Identifier identifier = new Identifier();
1114
            try
1115
            {
1116
                //System.out.println("converting docid '" + docid + "' to a guid.");
1117
                if(docid == null || docid.trim().equals("") || docid.trim().equals("null"))
1118
                {
1119
                    continue;
1120
                }
1121
                docid = docid.substring(0, docid.lastIndexOf("."));
1122
                identifier.setValue(im.getGUID(docid, im.getLatestRevForLocalId(docid)));
1123
            }
1124
            catch(Exception ex)
1125
            { //try to get the guid, if that doesn't work, just use the local id
1126
                //throw new ServiceFailure("1030", "Error getting guid for localId " + 
1127
                //        docid + ": " + ex.getMessage());\
1128
                
1129
                //skip it if the guid can't be found
1130
                continue;
1131
            }
1132
            
1133
            le.setIdentifier(identifier);
1134
            le.setIpAddress(ipAddress);
1135
            Calendar c = Calendar.getInstance();
1136
            String year = dateLogged.substring(0, 4);
1137
            String month = dateLogged.substring(5, 7);
1138
            String date = dateLogged.substring(8, 10);
1139
            //System.out.println("year: " + year + " month: " + month + " day: " + date);
1140
            c.set(new Integer(year).intValue(), new Integer(month).intValue(), new Integer(date).intValue());
1141
            Date logDate = c.getTime();
1142
            le.setDateLogged(logDate);
1143
            NodeReference memberNode = new NodeReference();
1144
            memberNode.setValue(ipAddress);
1145
            le.setMemberNode(memberNode);
1146
            Principal princ = new Principal();
1147
            princ.setValue(principal);
1148
            le.setPrincipal(princ);
1149
            le.setUserAgent("metacat/RESTService");
1150
            
1151
            if(event == null)
1152
            {
1153
                logs.add(le);
1154
            }
1155
            
1156
            if(event != null &&
1157
               e.toString().toLowerCase().trim().equals(event.toString().toLowerCase().trim()))
1158
            {
1159
              logs.add(le);
1160
            }
1161
        }
1162
        
1163
        log.setLogEntryList(logs);
1164
        logCrud.info("getLogRecords");
1165
        return log;
1166
    }
1167
    
1168
    /**
1169
     * parse a logEntry and get the relavent field from it
1170
     * @param fieldname
1171
     * @param entry
1172
     * @return
1173
     */
1174
    private String getLogEntryField(String fieldname, String entry)
1175
    {
1176
        String begin = "<" + fieldname + ">";
1177
        String end = "</" + fieldname + ">";
1178
        //System.out.println("looking for " + begin + " and " + end + " in entry " + entry);
1179
        String s = entry.substring(entry.indexOf(begin) + begin.length(), entry.indexOf(end));
1180
        //System.out.println("entry " + fieldname + " : " + s);
1181
        return s;
1182
    }
1183

    
1184
    /**
1185
     * get the system metadata for a document with a specified guid.
1186
     */
1187
public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
1188
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1189
            InvalidRequest, NotImplemented {
1190
        
1191
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
1192
        
1193
        // Retrieve the session information from the AuthToken
1194
        // If the session is expired, then the user is 'public'
1195
        final SessionData sessionData = getSessionData(token);
1196
                
1197
        try {
1198
            IdentifierManager im = IdentifierManager.getInstance();
1199
            final String localId = im.getSystemMetadataLocalId(guid.getValue());
1200
            
1201
            // Read system metadata from metacat's db
1202
            final InputStreamFromOutputStream<String> objectStream = 
1203
                new InputStreamFromOutputStream<String>() {
1204
                
1205
                @Override
1206
                public String produce(final OutputStream dataSink) throws Exception {
1207
                    //TODO: change to memory mapped IO so that exceptions get 
1208
                    //passed to the response correctly.
1209
                    try {
1210
                        String username = "public";
1211
                        String[] groupnames = null;
1212
                        if(sessionData != null)
1213
                        {
1214
                            username = sessionData.getUserName();
1215
                            groupnames = sessionData.getGroupNames();
1216
                        }
1217
                        
1218
                        handler.readFromMetacat(metacatUrl, null, 
1219
                                dataSink, localId, "xml",
1220
                                username, 
1221
                                groupnames, true, params);
1222
                    } catch (PropertyNotFoundException e) {
1223
                        e.printStackTrace();
1224
                        throw new ServiceFailure("1030", "Property not found while reading system metadata from metacat: " + e.getMessage());
1225
                    } catch (ClassNotFoundException e) {
1226
                        e.printStackTrace();
1227
                        throw new ServiceFailure("1030", "Class not found while reading system metadata from metacat: " + e.getMessage());
1228
                    } catch (IOException e) {
1229
                        e.printStackTrace();
1230
                        throw new ServiceFailure("1030", "IOException while reading system metadata from metacat: " + e.getMessage());
1231
                    } catch (SQLException e) {
1232
                        e.printStackTrace();
1233
                        throw new ServiceFailure("1030", "SQLException while reading system metadata from metacat: " + e.getMessage());
1234
                    } catch (McdbException e) {
1235
                        e.printStackTrace();
1236
                        throw new ServiceFailure("1030", "Metacat DB Exception while reading system metadata from metacat: " + e.getMessage());
1237
                    } catch (ParseLSIDException e) {
1238
                        e.printStackTrace();
1239
                        throw new NotFound("1020", "Error parsing LSID while reading system metadata from metacat: " + e.getMessage());
1240
                    } catch (InsufficientKarmaException e) {
1241
                        e.printStackTrace();
1242
                        throw new NotAuthorized("1000", "User not authorized for get() on system metadata: " + e.getMessage());
1243
                    }
1244

    
1245
                    return "Completed";
1246
                }
1247
            };
1248
            
1249
            // Deserialize the xml to create a SystemMetadata object
1250
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
1251
            String username = "public";
1252
            if(sessionData != null)
1253
            {
1254
                username = sessionData.getUserName();
1255
            }
1256
            EventLog.getInstance().log(metacatUrl,
1257
                    username, im.getLocalId(guid.getValue()), "read");
1258
            logCrud.info("getsystemmetadata D1GUID:" + guid.getValue()  + 
1259
                    ":D1SYSMETADATA:"+ localId + ":");
1260
            return sysmeta;
1261
            
1262
        } catch (McdbDocNotFoundException e) {
1263
            //e.printStackTrace();
1264
            throw new NotFound("1000", e.getMessage());
1265
        }                
1266
    }
1267
    
1268
    /**
1269
     * parse the date in the systemMetadata
1270
     * @param s
1271
     * @return
1272
     * @throws Exception
1273
     */
1274
    public Date parseDate(String s)
1275
      throws Exception
1276
    {
1277
        /* TODO:
1278
         * This method should be replaced by a DateFormatter
1279
         */
1280
        Date d = null;
1281
        int tIndex = s.indexOf("T");
1282
        int zIndex = s.indexOf("Z");
1283
        if(tIndex != -1 && zIndex != -1)
1284
        { //parse a date that looks like 2010-05-18T21:12:54.362Z
1285
            //System.out.println("original date: " + s);
1286
            
1287
            String date = s.substring(0, tIndex);
1288
            String year = date.substring(0, date.indexOf("-"));
1289
            String month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
1290
            String day = date.substring(date.lastIndexOf("-") + 1, date.length());
1291
            /*System.out.println("date: " + "year: " + new Integer(year).intValue() + 
1292
                    " month: " + new Integer(month).intValue() + " day: " + 
1293
                    new Integer(day).intValue());
1294
            */
1295
            String time = s.substring(tIndex + 1, zIndex);
1296
            String hour = time.substring(0, time.indexOf(":"));
1297
            String minute = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
1298
            String seconds = "00";
1299
            String milliseconds = "00";
1300
            if(time.indexOf(".") != -1)
1301
            {
1302
                seconds = time.substring(time.lastIndexOf(":") + 1, time.indexOf("."));
1303
                milliseconds = time.substring(time.indexOf(".") + 1, time.length());
1304
            }
1305
            else
1306
            {
1307
                seconds = time.substring(time.lastIndexOf(":") + 1, time.length());
1308
            }
1309
            /*System.out.println("time: " + "hour: " + new Integer(hour).intValue() + 
1310
                    " minute: " + new Integer(minute).intValue() + " seconds: " + 
1311
                    new Integer(seconds).intValue() + " milli: " + 
1312
                    new Integer(milliseconds).intValue());*/
1313
            
1314
            //d = DateFormat.getDateTimeInstance().parse(date + " " + time);
1315
            Calendar c = Calendar.getInstance(/*TimeZone.getTimeZone("GMT-0")*/TimeZone.getDefault());
1316
            c.set(new Integer(year).intValue(), new Integer(month).intValue() - 1, 
1317
                  new Integer(day).intValue(), new Integer(hour).intValue(), 
1318
                  new Integer(minute).intValue(), new Integer(seconds).intValue());
1319
            c.set(Calendar.MILLISECOND, new Integer(milliseconds).intValue());
1320
            d = new Date(c.getTimeInMillis());
1321
            //System.out.println("d: " + d);
1322
            return d;
1323
        }
1324
        else
1325
        {  //if it's not in the expected format, try the formatter
1326
            return DateFormat.getDateTimeInstance().parse(s);
1327
        }
1328
    }
1329

    
1330
    /*
1331
     * Look up the information on the session using the token provided in
1332
     * the AuthToken.  The Session should have all relevant user information.
1333
     * If the session has expired or is invalid, the 'public' session will
1334
     * be returned, giving the user anonymous access.
1335
     */
1336
    public static SessionData getSessionData(AuthToken token) {
1337
        SessionData sessionData = null;
1338
        String sessionId = "PUBLIC";
1339
        if (token != null) {
1340
            sessionId = token.getToken();
1341
        }
1342
        
1343
        // if the session id is registered in SessionService, get the
1344
        // SessionData for it. Otherwise, use the public session.
1345
        //System.out.println("sessionid: " + sessionId);
1346
        if (sessionId != null &&
1347
            !sessionId.toLowerCase().equals("public") &&
1348
            SessionService.getInstance().isSessionRegistered(sessionId)) 
1349
        {
1350
            sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
1351
        } else {
1352
            sessionData = SessionService.getInstance().getPublicSession();
1353
        }
1354
        
1355
        return sessionData;
1356
    }
1357

    
1358
    /** 
1359
     * Determine if a given object should be treated as an XML science metadata
1360
     * object. 
1361
     * 
1362
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
1363
     * 
1364
     * @param sysmeta the SystemMetadata describig the object
1365
     * @return true if the object should be treated as science metadata
1366
     */
1367
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
1368
        /*boolean scimeta = false;
1369
        //TODO: this should be read from a .properties file instead of being hard coded
1370
        switch (sysmeta.getObjectFormat()) {
1371
            case EML_2_1_0: scimeta = true; break;
1372
            case EML_2_0_1: scimeta = true; break;
1373
            case EML_2_0_0: scimeta = true; break;
1374
            case FGDC_STD_001_1_1999: scimeta = true; break;
1375
            case FGDC_STD_001_1998: scimeta = true; break;
1376
            case NCML_2_2: scimeta = true; break;
1377
            case DSPACE_METS_SIP_1_0: scimeta = true; break;
1378
        }
1379
        
1380
        return scimeta;*/
1381
        
1382
        return MetadataTypeRegister.isMetadataType(sysmeta.getObjectFormat());
1383
    }
1384

    
1385
    /**
1386
     * insert a data doc
1387
     * @param object
1388
     * @param guid
1389
     * @param sessionData
1390
     * @throws ServiceFailure
1391
     * @returns localId of the data object inserted
1392
     */
1393
    private String insertDataObject(InputStream object, Identifier guid, 
1394
            SessionData sessionData) throws ServiceFailure {
1395
        
1396
        String username = "public";
1397
        String[] groups = null;
1398
        if(sessionData != null)
1399
        {
1400
          username = sessionData.getUserName();
1401
          groups = sessionData.getGroupNames();
1402
        }
1403

    
1404
        // generate guid/localId pair for object
1405
        logMetacat.debug("Generating a guid/localId mapping");
1406
        IdentifierManager im = IdentifierManager.getInstance();
1407
        String localId = im.generateLocalId(guid.getValue(), 1);
1408

    
1409
        try {
1410
            logMetacat.debug("Case DATA: starting to write to disk.");
1411
            if (DocumentImpl.getDataFileLockGrant(localId)) {
1412
    
1413
                // Save the data file to disk using "localId" as the name
1414
                try {
1415
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
1416
    
1417
                    File dataDirectory = new File(datafilepath);
1418
                    dataDirectory.mkdirs();
1419
    
1420
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
1421
    
1422
                    // TODO: Check that the file size matches SystemMetadata
1423
                    //                        long size = newFile.length();
1424
                    //                        if (size == 0) {
1425
                    //                            throw new IOException("Uploaded file is 0 bytes!");
1426
                    //                        }
1427
    
1428
                    // Register the file in the database (which generates an exception
1429
                    // if the localId is not acceptable or other untoward things happen
1430
                    try {
1431
                        logMetacat.debug("Registering document...");
1432
                        DocumentImpl.registerDocument(localId, "BIN", localId,
1433
                                username, groups);
1434
                        logMetacat.debug("Registration step completed.");
1435
                    } catch (SQLException e) {
1436
                        //newFile.delete();
1437
                        logMetacat.debug("SQLE: " + e.getMessage());
1438
                        e.printStackTrace(System.out);
1439
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1440
                    } catch (AccessionNumberException e) {
1441
                        //newFile.delete();
1442
                        logMetacat.debug("ANE: " + e.getMessage());
1443
                        e.printStackTrace(System.out);
1444
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1445
                    } catch (Exception e) {
1446
                        //newFile.delete();
1447
                        logMetacat.debug("Exception: " + e.getMessage());
1448
                        e.printStackTrace(System.out);
1449
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1450
                    }
1451
    
1452
                    logMetacat.debug("Logging the creation event.");
1453
                    EventLog.getInstance().log(metacatUrl,
1454
                            username, localId, "create");
1455
    
1456
                    // Schedule replication for this data file
1457
                    logMetacat.debug("Scheduling replication.");
1458
                    ForceReplicationHandler frh = new ForceReplicationHandler(
1459
                            localId, "create", false, null);
1460
    
1461
                } catch (PropertyNotFoundException e) {
1462
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
1463
                }
1464
            }
1465
            return localId;
1466
        } catch (Exception e) {
1467
            // Could not get a lock on the document, so we can not update the file now
1468
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
1469
        }
1470
    }
1471

    
1472
    /**
1473
     * write a file to a stream
1474
     * @param dir
1475
     * @param fileName
1476
     * @param data
1477
     * @return
1478
     * @throws ServiceFailure
1479
     */
1480
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
1481
        throws ServiceFailure {
1482
        
1483
        File newFile = new File(dir, fileName);
1484
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
1485

    
1486
        try {
1487
            if (newFile.createNewFile()) {
1488
                // write data stream to desired file
1489
                OutputStream os = new FileOutputStream(newFile);
1490
                long length = IOUtils.copyLarge(data, os);
1491
                os.flush();
1492
                os.close();
1493
            } else {
1494
                logMetacat.debug("File creation failed, or file already exists.");
1495
                throw new ServiceFailure("1190", "File already exists: " + fileName);
1496
            }
1497
        } catch (FileNotFoundException e) {
1498
            logMetacat.debug("FNF: " + e.getMessage());
1499
            throw new ServiceFailure("1190", "File not found: " + fileName + " " 
1500
                    + e.getMessage());
1501
        } catch (IOException e) {
1502
            logMetacat.debug("IOE: " + e.getMessage());
1503
            throw new ServiceFailure("1190", "File was not written: " + fileName 
1504
                    + " " + e.getMessage());
1505
        }
1506

    
1507
        return newFile;
1508
    }
1509

    
1510
    /**
1511
     * insert a systemMetadata doc, return the localId of the sysmeta
1512
     */
1513
    private String insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData) 
1514
        throws ServiceFailure 
1515
    {
1516
        logMetacat.debug("Starting to insert SystemMetadata...");
1517
    
1518
        // generate guid/localId pair for sysmeta
1519
        Identifier sysMetaGuid = new Identifier();
1520
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
1521
        sysmeta.setDateSysMetadataModified(new Date());
1522
        System.out.println("****inserting new system metadata with modified date " + 
1523
                sysmeta.getDateSysMetadataModified());
1524

    
1525
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
1526
        System.out.println("sysmeta: " + xml);
1527
        String localId = insertDocument(xml, sysMetaGuid, sessionData, true);
1528
        System.out.println("sysmeta inserted with localId " + localId);
1529
        //insert the system metadata doc id into the systemmetadata table to 
1530
        //link it to the data or metadata document
1531
        IdentifierManager.getInstance().createSystemMetadataMapping(
1532
                sysmeta.getIdentifier().getValue(), sysMetaGuid.getValue());
1533
        return localId;
1534
    }
1535
    
1536
    /**
1537
     * update a systemMetadata doc
1538
     */
1539
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
1540
      throws ServiceFailure
1541
    {
1542
        try
1543
        {
1544
            String smId = IdentifierManager.getInstance().getSystemMetadataLocalId(sm.getIdentifier().getValue());
1545
            sm.setDateSysMetadataModified(new Date());
1546
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
1547
            String localId = updateDocument(xml, sm.getIdentifier(), null, sessionData, true);
1548
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
1549
        }
1550
        catch(Exception e)
1551
        {
1552
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getMessage());
1553
        }
1554
    }
1555
    
1556
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
1557
        throws ServiceFailure
1558
    {
1559
        return insertDocument(xml, guid, sessionData, false);
1560
    }
1561
    
1562
    /**
1563
     * insert a document
1564
     * NOTE: this method shouldn't be used from the update or create() methods.  
1565
     * we shouldn't be putting the science metadata or data objects into memory.
1566
     */
1567
    private String insertDocument(String xml, Identifier guid, SessionData sessionData,
1568
            boolean isSystemMetadata)
1569
        throws ServiceFailure
1570
    {
1571
        return insertOrUpdateDocument(xml, guid, sessionData, "insert", isSystemMetadata);
1572
    }
1573
    
1574
    /**
1575
     * insert a document from a stream
1576
     */
1577
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
1578
      throws IOException, ServiceFailure
1579
    {
1580
        //HACK: change this eventually.  we should not be converting the stream to a string
1581
        String xml = IOUtils.toString(is);
1582
        return insertDocument(xml, guid, sessionData);
1583
    }
1584
    
1585
    /**
1586
     * update a document
1587
     * NOTE: this method shouldn't be used from the update or create() methods.  
1588
     * we shouldn't be putting the science metadata or data objects into memory.
1589
     */
1590
    private String updateDocument(String xml, Identifier obsoleteGuid, 
1591
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1592
        throws ServiceFailure
1593
    {
1594
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update", isSystemMetadata);
1595
    }
1596
    
1597
    /**
1598
     * update a document from a stream
1599
     */
1600
    private String updateDocument(InputStream is, Identifier obsoleteGuid, 
1601
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1602
      throws IOException, ServiceFailure
1603
    {
1604
        //HACK: change this eventually.  we should not be converting the stream to a string
1605
        String xml = IOUtils.toString(is);
1606
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData, isSystemMetadata);
1607
        IdentifierManager im = IdentifierManager.getInstance();
1608
        if(guid != null)
1609
        {
1610
          im.createMapping(guid.getValue(), localId);
1611
        }
1612
        return localId;
1613
    }
1614
    
1615
    /**
1616
     * insert a document, return the id of the document that was inserted
1617
     */
1618
    protected String insertOrUpdateDocument(String xml, Identifier guid, 
1619
            SessionData sessionData, String insertOrUpdate, boolean isSystemMetadata) 
1620
        throws ServiceFailure {
1621
        logMetacat.debug("Starting to insert xml document...");
1622
        IdentifierManager im = IdentifierManager.getInstance();
1623

    
1624
        // generate guid/localId pair for sysmeta
1625
        String localId = null;
1626
        if(insertOrUpdate.equals("insert"))
1627
        {
1628
            localId = im.generateLocalId(guid.getValue(), 1, isSystemMetadata);
1629
        }
1630
        else
1631
        {
1632
            //localid should already exist in the identifier table, so just find it
1633
            try
1634
            {
1635
                System.out.println("updating guid " + guid.getValue());
1636
                if(!isSystemMetadata)
1637
                {
1638
                    System.out.println("looking in identifier table for guid " + guid.getValue());
1639
                    localId = im.getLocalId(guid.getValue());
1640
                }
1641
                else
1642
                {
1643
                    System.out.println("looking in systemmetadata table for guid " + guid.getValue());
1644
                    localId = im.getSystemMetadataLocalId(guid.getValue());
1645
                }
1646
                System.out.println("localId: " + localId);
1647
                //increment the revision
1648
                String docid = localId.substring(0, localId.lastIndexOf("."));
1649
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
1650
                int rev = new Integer(revS).intValue();
1651
                rev++;
1652
                docid = docid + "." + rev;
1653
                localId = docid;
1654
                System.out.println("incremented localId: " + localId);
1655
            }
1656
            catch(McdbDocNotFoundException e)
1657
            {
1658
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
1659
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
1660
            }
1661
        }
1662
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
1663
                localId);
1664

    
1665
        String[] action = new String[1];
1666
        action[0] = insertOrUpdate;
1667
        params.put("action", action);
1668
        String[] docid = new String[1];
1669
        docid[0] = localId;
1670
        params.put("docid", docid);
1671
        String[] doctext = new String[1];
1672
        doctext[0] = xml;
1673
        logMetacat.debug(doctext[0]);
1674
        params.put("doctext", doctext);
1675
        
1676
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
1677
        // onto output stream, or alternatively, capture that and parse it to 
1678
        // generate the right exceptions
1679
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
1680
        //PrintWriter pw = new PrintWriter(output);
1681
        String username = "public";
1682
        String[] groupnames = null;
1683
        if(sessionData != null)
1684
        {
1685
            username = sessionData.getUserName();
1686
            groupnames = sessionData.getGroupNames();
1687
        }
1688
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
1689
                            null, params, username, groupnames);
1690
        if(result.indexOf("<error>") != -1)
1691
        {
1692
            throw new ServiceFailure("1000", "Error inserting or updating document: " + result);
1693
        }
1694
        //String outputS = new String(output.toByteArray());
1695
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
1696
        logMetacat.debug("Finsished inserting xml document with id " + localId);
1697
        return localId;
1698
    }
1699
    
1700
    /**
1701
     * serialize a dataone type
1702
     */
1703
    private void serializeServiceType(Class type, Object object, OutputStream out)
1704
        throws JiBXException
1705
    {
1706
        IBindingFactory bfact = BindingDirectory.getFactory(type);
1707
        IMarshallingContext mctx = bfact.createMarshallingContext();
1708
        mctx.marshalDocument(object, "UTF-8", null, out);
1709
    }
1710
    
1711
    /**
1712
     * serialize a system metadata doc
1713
     * @param sysmeta
1714
     * @return
1715
     * @throws ServiceFailure
1716
     */
1717
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta) 
1718
        throws ServiceFailure {
1719
        IBindingFactory bfact;
1720
        ByteArrayOutputStream sysmetaOut = null;
1721
        try {
1722
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
1723
            IMarshallingContext mctx = bfact.createMarshallingContext();
1724
            sysmetaOut = new ByteArrayOutputStream();
1725
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
1726
        } catch (JiBXException e) {
1727
            e.printStackTrace();
1728
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
1729
        }
1730
        
1731
        return sysmetaOut;
1732
    }
1733
    
1734
    /**
1735
     * deserialize a system metadata doc
1736
     * @param xml
1737
     * @return
1738
     * @throws ServiceFailure
1739
     */
1740
    public static SystemMetadata deserializeSystemMetadata(InputStream xml) 
1741
        throws ServiceFailure {
1742
        try {
1743
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
1744
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1745
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
1746
            return sysmeta;
1747
        } catch (JiBXException e) {
1748
            e.printStackTrace();
1749
            throw new ServiceFailure("1190", "Failed to deserialize and insert SystemMetadata: " + e.getMessage());
1750
        }    
1751
    }
1752
    
1753
    /**
1754
     * return an MD5 checksum for the stream
1755
     * @param is
1756
     * @return
1757
     */
1758
    private String checksum(InputStream is)
1759
        throws Exception
1760
    {
1761
        return checksum(is, "MD5");
1762
    }
1763
    
1764
    /**
1765
     * produce a checksum for item using the given algorithm
1766
     */
1767
    private String checksum(InputStream is, String algorithm)
1768
      throws Exception
1769
    {        
1770
        byte[] buffer = new byte[1024];
1771
        MessageDigest complete = MessageDigest.getInstance(algorithm);
1772
        int numRead;
1773
        
1774
        do 
1775
        {
1776
          numRead = is.read(buffer);
1777
          if (numRead > 0) 
1778
          {
1779
            complete.update(buffer, 0, numRead);
1780
          }
1781
        } while (numRead != -1);
1782
        
1783
        
1784
        return getHex(complete.digest());
1785
    }
1786
    
1787
    /**
1788
     * convert a byte array to a hex string
1789
     */
1790
    private static String getHex( byte [] raw ) 
1791
    {
1792
        final String HEXES = "0123456789ABCDEF";
1793
        if ( raw == null ) {
1794
          return null;
1795
        }
1796
        final StringBuilder hex = new StringBuilder( 2 * raw.length );
1797
        for ( final byte b : raw ) {
1798
          hex.append(HEXES.charAt((b & 0xF0) >> 4))
1799
             .append(HEXES.charAt((b & 0x0F)));
1800
        }
1801
        return hex.toString();
1802
    }
1803
    
1804
    /**
1805
     * parse the metacat date which looks like 2010-06-08 (YYYY-MM-DD) into
1806
     * a proper date object
1807
     * @param date
1808
     * @return
1809
     */
1810
    private Date parseMetacatDate(String date)
1811
    {
1812
        String year = date.substring(0, 4);
1813
        String month = date.substring(5, 7);
1814
        String day = date.substring(8, 10);
1815
        Calendar c = Calendar.getInstance(TimeZone.getDefault());
1816
        c.set(new Integer(year).intValue(), 
1817
              new Integer(month).intValue(), 
1818
              new Integer(day).intValue());
1819
        System.out.println("time in parseMetacatDate: " + c.getTime());
1820
        return c.getTime();
1821
    }
1822
    
1823
    /**
1824
     * find the size (in bytes) of a stream
1825
     * @param is
1826
     * @return
1827
     * @throws IOException
1828
     */
1829
    private long sizeOfStream(InputStream is)
1830
        throws IOException
1831
    {
1832
        long size = 0;
1833
        byte[] b = new byte[1024];
1834
        int numread = is.read(b, 0, 1024);
1835
        while(numread != -1)
1836
        {
1837
            size += numread;
1838
            numread = is.read(b, 0, 1024);
1839
        }
1840
        return size;
1841
    }
1842
    
1843
    /**
1844
     * create system metadata with a specified id, doc and format
1845
     */
1846
    private SystemMetadata createSystemMetadata(String localId, AuthToken token)
1847
      throws Exception
1848
    {
1849
        IdentifierManager im = IdentifierManager.getInstance();
1850
        Hashtable<String, Object> docInfo = im.getDocumentInfo(localId);
1851
        
1852
        //get the document text
1853
        int rev = im.getLatestRevForLocalId(localId);
1854
        Identifier identifier = new Identifier();
1855
        identifier.setValue(im.getGUID(localId, rev));
1856
        InputStream is = this.get(token, identifier);
1857
        
1858
        SystemMetadata sm = new SystemMetadata();
1859
        //set the id
1860
        sm.setIdentifier(identifier);
1861
        
1862
        //set the object format
1863
        String doctype = (String)docInfo.get("doctype");
1864
        ObjectFormat format = ObjectFormat.convert((String)docInfo.get("doctype"));
1865
        if(format == null)
1866
        {
1867
            if(doctype.trim().equals("BIN"))
1868
            {
1869
                format = ObjectFormat.OCTET_STREAM;
1870
            }
1871
            else
1872
            {
1873
                format = ObjectFormat.convert("text/plain");
1874
            }
1875
        }
1876
        sm.setObjectFormat(format);
1877
        
1878
        //create the checksum
1879
        String checksumS = checksum(is);
1880
        ChecksumAlgorithm ca = ChecksumAlgorithm.convert("MD5");
1881
        Checksum checksum = new Checksum();
1882
        checksum.setValue(checksumS);
1883
        checksum.setAlgorithm(ca);
1884
        sm.setChecksum(checksum);
1885
        
1886
        //set the size
1887
        is = this.get(token, identifier);
1888
        sm.setSize(sizeOfStream(is));
1889
        
1890
        //submitter
1891
        Principal p = new Principal();
1892
        p.setValue((String)docInfo.get("user_owner"));
1893
        sm.setSubmitter(p);
1894
        sm.setRightsHolder(p);
1895
        try
1896
        {
1897
            Date dateCreated = parseMetacatDate((String)docInfo.get("date_created"));
1898
            sm.setDateUploaded(dateCreated);
1899
            Date dateUpdated = parseMetacatDate((String)docInfo.get("date_updated"));
1900
            sm.setDateSysMetadataModified(dateUpdated);
1901
        }
1902
        catch(Exception e)
1903
        {
1904
            System.out.println("POSSIBLE ERROR: couldn't parse a date: " + e.getMessage());
1905
            Date dateCreated = new Date();
1906
            sm.setDateUploaded(dateCreated);
1907
            Date dateUpdated = new Date();
1908
            sm.setDateSysMetadataModified(dateUpdated);
1909
        }
1910
        NodeReference nr = new NodeReference();
1911
        //TODO: this should be set to be something more meaningful once the registry is up
1912
        nr.setValue("metacat");
1913
        sm.setOriginMemberNode(nr);
1914
        sm.setAuthoritativeMemberNode(nr);
1915
        return sm;
1916
    }
1917
}
(1-1/3)