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:ss.SSSZ");
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("1620", "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("1100", "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
        if (isScienceMetadata) {
319
            // CASE METADATA:
320
            try {
321
                //System.out.println("CrudService: inserting document with guid " + guid.getValue());
322
                this.insertDocument(object, guid, sessionData);
323
                localId = im.getLocalId(guid.getValue());
324
            } catch (IOException e) {
325
                String msg = "Could not create string from XML stream: " +
326
                    " " + e.getMessage();
327
                logMetacat.debug(msg);
328
                throw new ServiceFailure("1190", msg);
329
            } catch(Exception e) {
330
                String msg = "Unexpected error in CrudService.create: " + e.getMessage();
331
                logMetacat.debug(msg);
332
                throw new ServiceFailure("1190", msg);
333
            }
334
            
335

    
336
        } else {
337
            // DEFAULT CASE: DATA (needs to be checked and completed)
338
            localId = insertDataObject(object, guid, sessionData);
339
            
340
        }
341

    
342
        // For Metadata and Data, insert the system metadata into the object store too
343
        String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
344
        //get the document info.  add any access params for the sysmeta too
345
        //System.out.println("looking for access records to add for system " +
346
        //    "metadata who's parent doc's  local id is " + localId);
347
        try
348
        {
349
            Hashtable<String, Object> h = im.getDocumentInfo(localId.substring(0, localId.lastIndexOf(".")));
350
            Vector v = (Vector)h.get("access");
351
            for(int i=0; i<v.size(); i++)
352
            {
353
                Hashtable ah = (Hashtable)v.elementAt(i);
354
                String principal = (String)ah.get("principal_name");
355
                String permission = (String)ah.get("permission");
356
                String permissionType = (String)ah.get("permission_type");
357
                String permissionOrder = (String)ah.get("permission_order");
358
                int perm = new Integer(permission).intValue();
359
                //System.out.println("found access record for principal " + principal);
360
                //System.out.println("permission: " + perm + " perm_type: " + permissionType + 
361
                //    " perm_order: " + permissionOrder);
362
                this.setAccess(token, guid, principal, perm, permissionType, permissionOrder, true);
363
            }
364
        }
365
        catch(Exception e)
366
        {
367
            logMetacat.error("Error setting permissions on System Metadata object " + 
368
                    " with id " + sysMetaLocalId + ": " + e.getMessage());
369
            //TODO: decide if this error should cancel the entire create or
370
            //if it should continue with just a logged error.
371
        }
372
        
373
        
374
        logMetacat.debug("Returning from CrudService.create()");
375
        EventLog.getInstance().log(metacatUrl,
376
                username, localId, "create");
377
        logCrud.info("create D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
378
                ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
379
        return guid;
380
    }
381
    
382
    /**
383
     * update an existing object with a new object.  Change the system metadata
384
     * to reflect the changes and update it as well.
385
     */
386
    public Identifier update(AuthToken token, Identifier guid, 
387
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta) 
388
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
389
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
390
            NotImplemented {
391
        try
392
        {
393
            SessionData sessionData = getSessionData(token);
394
            
395
            //find the old systemmetadata (sm.old) document id (the one linked to obsoletedGuid)
396
            SystemMetadata sm = getSystemMetadata(token, obsoletedGuid);
397
            //change sm.old's obsoletedBy field 
398
            List l = sm.getObsoletedByList();
399
            l.add(guid);
400
            sm.setObsoletedByList(l);
401
            //update sm.old
402
            updateSystemMetadata(sm, sessionData);
403
            
404
            //change the obsoletes field of the new systemMetadata (sm.new) to point to the id of the old one
405
            sysmeta.addObsolete(obsoletedGuid);
406
            //insert sm.new
407
            String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
408
            String localId;
409
            
410
            boolean isScienceMetadata = isScienceMetadata(sysmeta);
411
            if(isScienceMetadata)
412
            {
413
                //update the doc
414
                localId = updateDocument(object, obsoletedGuid, guid, sessionData, false);
415
            }
416
            else
417
            {
418
                //update a data file, not xml
419
                localId = insertDataObject(object, guid, sessionData);
420
            }
421
            
422
            IdentifierManager im = IdentifierManager.getInstance();
423
            String username = "public";
424
            if(sessionData != null)
425
            {
426
                username = sessionData.getUserName();
427
            }
428
            EventLog.getInstance().log(metacatUrl,
429
                    username, im.getLocalId(guid.getValue()), "update");
430
            logCrud.info("update D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
431
                    ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
432
            return guid;
433
        }
434
        catch(Exception e)
435
        {
436
            throw new ServiceFailure("1310", "Error updating document in CrudService: " + e.getMessage());
437
        }
438
    }
439
    
440
    /**
441
     * set access permissions on both the science metadata and system metadata
442
     */
443
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
444
            String permissionType, String permissionOrder)
445
      throws ServiceFailure
446
    {
447
        setAccess(token, id, principal, permission, permissionType, permissionOrder, true);
448
    }
449
    
450
    /**
451
     * set access control on the doc
452
     * @param token
453
     * @param id
454
     * @param principal
455
     * @param permission
456
     */
457
    public void setAccess(AuthToken token, Identifier id, String principal, int permission,
458
      String permissionType, String permissionOrder, boolean setSystemMetadata)
459
      throws ServiceFailure
460
    {
461
        String perm = "";
462
        if(permission >= 4)
463
        {
464
            perm = "read";
465
        }
466
        if(permission >= 6)
467
        {
468
            perm = "write";
469
        }
470
        //System.out.println("perm in setAccess: " + perm);
471
        //System.out.println("permission in setAccess: " + permission);
472
        setAccess(token, id, principal, perm, permissionType, permissionOrder,
473
                setSystemMetadata);
474
       
475
    }
476
    
477
    /**
478
     * set the permission on the document
479
     * @param token
480
     * @param principal
481
     * @param permission
482
     * @param permissionType
483
     * @param permissionOrder
484
     * @return
485
     */
486
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
487
            String permissionType, String permissionOrder, boolean setSystemMetadata)
488
      throws ServiceFailure
489
    {
490
        /* TODO:
491
         * This is also not part of the D1 Crud spec.  This method is needed for
492
         * systems such as metacat where access to objects is controlled by
493
         * and ACL.  Higher level decisions need to be made about how this
494
         * should work within D1.
495
         */
496
        try
497
        {
498
            final SessionData sessionData = getSessionData(token);
499
            if(sessionData == null)
500
            {
501
                throw new ServiceFailure("1000", "User must be logged in to set access.");
502
            }
503
            IdentifierManager im = IdentifierManager.getInstance();
504
            String docid = im.getLocalId(id.getValue());
505
        
506
            String permNum = "0";
507
            if(permission.equals("read"))
508
            {
509
                permNum = "4";
510
            }
511
            else if(permission.equals("write"))
512
            {
513
                permNum = "6";
514
            }
515
            System.out.println("user " + sessionData.getUserName() + 
516
                    " is setting access level " + permNum + " for permission " + 
517
                    permissionType + " on doc with localid " + docid);
518
            handler.setAccess(metacatUrl, sessionData.getUserName(), docid, 
519
                    principal, permNum, permissionType, permissionOrder);
520
            if(setSystemMetadata)
521
            {
522
                //set the same perms on the system metadata doc
523
                String smlocalid = im.getSystemMetadataLocalId(id.getValue());
524
                System.out.println("setting access on SM doc with localid " + smlocalid);
525
                //cs.setAccess(token, smid, principal, permission, permissionType, permissionOrder);
526
                handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid,
527
                        principal, permNum, permissionType, permissionOrder);
528
            }
529
            String username = "public";
530
            if(sessionData != null)
531
            {
532
                username = sessionData.getUserName();
533
            }
534
            EventLog.getInstance().log(metacatUrl,
535
                    username, im.getLocalId(id.getValue()), "setAccess");
536
            logCrud.info("setAccess");
537
        }
538
        catch(Exception e)
539
        {
540
            e.printStackTrace();
541
            throw new ServiceFailure("1000", "Could not set access on the document with id " + id.getValue());
542
        }
543
    }
544
    
545
    /**
546
     *  Retrieve the list of objects present on the MN that match the calling 
547
     *  parameters. This method is required to support the process of Member 
548
     *  Node synchronization. At a minimum, this method should be able to 
549
     *  return a list of objects that match:
550
     *  startTime <= SystemMetadata.dateSysMetadataModified
551
     *  but is expected to also support date range (by also specifying endTime), 
552
     *  and should also support slicing of the matching set of records by 
553
     *  indicating the starting index of the response (where 0 is the index 
554
     *  of the first item) and the count of elements to be returned.
555
     *  
556
     *  If startTime or endTime is null, the query is not restricted by that parameter.
557
     *  
558
     * @see http://mule1.dataone.org/ArchitectureDocs/mn_api_replication.html#MN_replication.listObjects
559
     * @param token
560
     * @param startTime
561
     * @param endTime
562
     * @param objectFormat
563
     * @param replicaStatus
564
     * @param start
565
     * @param count
566
     * @return ObjectList
567
     * @throws NotAuthorized
568
     * @throws InvalidRequest
569
     * @throws NotImplemented
570
     * @throws ServiceFailure
571
     * @throws InvalidToken
572
     */
573
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
574
        ObjectFormat objectFormat, boolean replicaStatus, int start, int count)
575
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
576
    {
577
      ObjectList ol = new ObjectList();
578
      final SessionData sessionData = getSessionData(token);
579
      int totalAfterQuery = 0;
580
      
581
      try
582
      {
583
          if (PropertyService.getProperty("database.queryCacheOn").equals("true"))
584
          {
585
              //System.out.println("the string stored into cache is "+ resultsetBuffer.toString());
586
              DBQuery.clearQueryResultCache();
587
          }
588
      }
589
      catch (PropertyNotFoundException e1)
590
      {
591
          //just don't do anything
592
      }
593
      
594
      try
595
      {
596
          //TODO: Milliseconds need to be added to the dateFormat
597
          System.out.println("=========== Listing Objects =============");
598
          System.out.println("Current server time is: " + new Date());
599
          if(startTime != null)
600
          {
601
              System.out.println("query start time is " + startTime);
602
          }
603
          if(endTime != null)
604
          {
605
              System.out.println("query end time is " + endTime);
606
          }
607
          params.clear();
608
          params.put("returndoctype", new String[] {PropertyService.getProperty("crudService.listObjects.ReturnDoctype")});
609
          params.put("qformat", new String[] {PropertyService.getProperty("crudService.listObjects.QFormat")});
610
          params.put("returnfield", new String[] {
611
                  PropertyService.getProperty("crudService.listObjects.ReturnField.1"), 
612
                  PropertyService.getProperty("crudService.listObjects.ReturnField.2"),
613
                  PropertyService.getProperty("crudService.listObjects.ReturnField.3"),
614
                  PropertyService.getProperty("crudService.listObjects.ReturnField.4"),
615
                  PropertyService.getProperty("crudService.listObjects.ReturnField.5"),
616
                  PropertyService.getProperty("crudService.listObjects.ReturnField.6"),
617
                  PropertyService.getProperty("crudService.listObjects.ReturnField.7"),
618
                  });
619
          params.put("anyfield", new String[] {PropertyService.getProperty("crudService.listObjects.anyfield")});
620
          
621
          /*System.out.println("query is: metacatUrl: " + metacatUrl + " user: " + sessionData.getUserName() +
622
                  " sessionid: " + sessionData.getId() + " params: ");
623
          String url = metacatUrl + "/metacat?action=query&sessionid=" + sessionData.getId();
624
          Enumeration keys = params.keys();
625
          while(keys.hasMoreElements())
626
          {
627
              String key = (String)keys.nextElement();
628
              String[] parr = params.get(key);
629
              for(int i=0; i<parr.length; i++)
630
              {
631
                  System.out.println("param " + key + ": " + parr[i]);
632
                  url += "&" + key + "=" + parr[i] ;
633
              }
634
          }
635
          System.out.println("query url: " + url);
636
          */
637
          String username = "public";
638
          String[] groups = null;
639
          String sessionid = "";
640
          if(sessionData != null)
641
          {
642
              username = sessionData.getUserName();
643
              groups = sessionData.getGroupNames();
644
              sessionid = sessionData.getId();
645
          }
646
          
647
          MetacatResultSet rs = handler.query(metacatUrl, params, username, 
648
                  groups, sessionid);
649
          List docs = rs.getDocuments();
650
          
651
          System.out.println("query returned " + docs.size() + " documents.");
652
          Vector<Document> docCopy = new Vector<Document>();
653
          
654
          //preparse the list to remove any that don't match the query params
655
          /* TODO: this type of query/subquery processing is probably not scalable
656
           * to larger object stores.  This code should be revisited.  The metacat
657
           * query handler should probably be altered to handle the type of query 
658
           * done here.
659
           */ 
660
          for(int i=0; i<docs.size(); i++)
661
          {
662
              Document d = (Document)docs.get(i);
663
              
664
              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
665
              
666
              if(returnedObjectFormat != null && 
667
                 objectFormat != null && 
668
                 !objectFormat.toString().trim().equals(returnedObjectFormat.toString().trim()))
669
              { //make sure the objectFormat is the one specified
670
                  continue;
671
              }
672
              
673
              String dateSMM = d.getField("dateSysMetadataModified");
674
              if((startTime != null || endTime != null) && dateSMM == null)
675
              {  //if startTime or endTime are not null, we need a date to compare to
676
                  continue;
677
              }
678
              
679
              //date parse
680
              Date dateSysMetadataModified = null;
681
              if(dateSMM != null)
682
              {
683

    
684
                  /*                  
685
                  if(dateSMM.indexOf(".") != -1)
686
                  {  //strip the milliseconds
687
                      //TODO: don't do this. we need milliseconds now.
688
                      //TODO: explore ISO 8601 to figure out milliseconds
689
                      dateSMM = dateSMM.substring(0, dateSMM.indexOf(".")) + 'Z';
690
                  }
691
                  */
692
                  //System.out.println("dateSMM: " + dateSMM);
693
                  //dateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
694
                  try
695
                  {   //the format we want
696
                      dateSysMetadataModified = dateFormat.parse(dateSMM);
697
                  }
698
                  catch(java.text.ParseException pe)
699
                  {   //try another legacy format
700
                      DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.S'Z'");
701
                      dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
702
                      dateSysMetadataModified = dateFormat2.parse(dateSMM);
703
                  }                  
704
              }
705
              
706
              /*System.out.println("====================================");
707
              System.out.println("doc number " + i);
708
              System.out.println("docid: " + d.docid);
709
              System.out.println("guid: " + d.getField("identifier").trim());
710
              System.out.println("dateSMM: " + dateSMM);
711
              System.out.println("dateSysMetadataModified: " + dateSysMetadataModified);
712
              System.out.println("startTime: " + startTime);
713
              System.out.println("endtime: " + endTime);*/
714
              
715
              int startDateComparison = 0;
716
              int endDateComparison = 0;
717
              if(startTime != null)
718
              {
719
                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
720
                  zTime.setTime(startTime);
721
                  startTime = zTime.getTime();
722
                  
723
                  if(dateSysMetadataModified == null)
724
                  {
725
                      startDateComparison = -1;
726
                  }
727
                  else
728
                  {
729
                      startDateComparison = dateSysMetadataModified.compareTo(startTime);
730
                  }
731
                  //System.out.println("startDateCom: " + startDateComparison);
732
              }
733
              else
734
              {
735
                  startDateComparison = 1;
736
              }
737
              
738
              if(endTime != null)
739
              {
740
                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
741
                  zTime.setTime(endTime);
742
                  endTime = zTime.getTime();
743
                  
744
                  if(dateSysMetadataModified == null)
745
                  {
746
                      endDateComparison = 1;
747
                  }
748
                  else
749
                  {
750
                      endDateComparison = dateSysMetadataModified.compareTo(endTime);
751
                  }
752
                  //System.out.println("endDateCom: " + endDateComparison);
753
              }
754
              else
755
              {
756
                  endDateComparison = -1;
757
              }
758
              
759
              
760
              if(startDateComparison < 0 || endDateComparison > 0)
761
              { 
762
                  continue;                  
763
              }
764
              
765
              docCopy.add((Document)docs.get(i));
766
          } //end pre-parse
767
          
768
          docs = docCopy;
769
          totalAfterQuery = docs.size();
770
          //System.out.println("total after subquery: " + totalAfterQuery);
771
          
772
          //make sure we don't run over the end
773
          int end = start + count;
774
          if(end > docs.size())
775
          {
776
              end = docs.size();
777
          }
778
          
779
          for(int i=start; i<end; i++)
780
          {
781
              //get the document from the result
782
              Document d = (Document)docs.get(i);
783
              //System.out.println("processing doc " + d.docid);
784
              
785
              String dateSMM = d.getField("dateSysMetadataModified");
786
              //System.out.println("dateSMM: " + dateSMM);
787
              //System.out.println("parsed date: " + parseDate(dateSMM));
788
              Date dateSysMetadataModified = null;
789
              if(dateSMM != null)
790
              {
791
                  try
792
                  {
793
                      dateSysMetadataModified = parseDate(dateSMM);
794
                  }
795
                  catch(Exception e)
796
                  { //if we fail to parse the date, just ignore the value
797
                      dateSysMetadataModified = null;
798
                  }
799
              }
800
              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
801
                
802
              
803
              ObjectInfo info = new ObjectInfo();
804
              //add the fields to the info object
805
              Checksum cs = new Checksum();
806
              cs.setValue(d.getField("checksum"));
807
              String csalg = d.getField("algorithm");
808
              if(csalg == null)
809
              {
810
                  csalg = "MD5";
811
              }
812
              ChecksumAlgorithm ca = ChecksumAlgorithm.convert(csalg);
813
              cs.setAlgorithm(ca);
814
              info.setChecksum(cs);
815
              info.setDateSysMetadataModified(dateSysMetadataModified);
816
              Identifier id = new Identifier();
817
              id.setValue(d.getField("identifier").trim());
818
              info.setIdentifier(id);
819
              info.setObjectFormat(returnedObjectFormat);
820
              String size = d.getField("size");
821
              if(size != null)
822
              {
823
                  info.setSize(new Long(size.trim()).longValue());
824
              }
825
              //add the ObjectInfo to the ObjectList
826
              //logCrud.info("objectFormat: " + info.getObjectFormat().toString());
827
              //logCrud.info("id: " + info.getIdentifier().getValue());
828
              
829
              if(info.getIdentifier().getValue() != null)
830
              { //id can be null from tests.  should not happen in production.
831
                  if((info.getObjectFormat() != null && !info.getObjectFormat().toString().trim().equals("")))
832
                  { //objectFormat needs to not be null and not be an empty string
833
                    ol.addObjectInfo(info);
834
                  }
835
                  else
836
                  {
837
                      logCrud.info("Not adding object with null objectFormat" + info.getIdentifier().getValue().toString());
838
                  }
839
              }
840
             
841
          }
842
      }
843
      catch(Exception e)
844
      {
845
          e.printStackTrace();
846
          logCrud.error("Error creating ObjectList: " + e.getMessage() + " cause: " + e.getCause());
847
          throw new ServiceFailure("1580", "Error retrieving ObjectList: " + e.getMessage());
848
      }
849
      String username = "public";
850
      if(sessionData != null)
851
      {
852
          username = sessionData.getUserName();
853
      }
854
      EventLog.getInstance().log(metacatUrl,
855
              username, null, "read");
856
      logCrud.info("listObjects");
857
      if(totalAfterQuery < count)
858
      {
859
          count = totalAfterQuery;
860
      }
861
      ol.setCount(count);
862
      ol.setStart(start);
863
      ol.setTotal(totalAfterQuery);
864
      return ol;
865
    }
866
    
867
    /**
868
     * Call listObjects with the default values for replicaStatus (true), start (0),
869
     * and count (1000).
870
     * @param token
871
     * @param startTime
872
     * @param endTime
873
     * @param objectFormat
874
     * @return
875
     * @throws NotAuthorized
876
     * @throws InvalidRequest
877
     * @throws NotImplemented
878
     * @throws ServiceFailure
879
     * @throws InvalidToken
880
     */
881
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
882
        ObjectFormat objectFormat)
883
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
884
    {
885
       return listObjects(token, startTime, endTime, objectFormat, true, 0, 1000);
886
    }
887

    
888
    /**
889
     * Delete a document. 
890
     */
891
    public Identifier delete(AuthToken token, Identifier guid)
892
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
893
            NotImplemented, InvalidRequest {
894
        logCrud.info("delete");
895
        
896
        if(token == null || token.getToken().equals("publid"))
897
        {
898
            throw new NotAuthorized("1320", "You must be logged in to delete records.");
899
        }
900
        
901
        if(guid == null || guid.getValue().trim().equals(""))
902
        {
903
            throw new InvalidRequest("1322", "No GUID specified in CrudService.delete()");
904
        }
905
        final SessionData sessionData = getSessionData(token);
906
        IdentifierManager manager = IdentifierManager.getInstance();
907
        
908
        String docid;
909
        try
910
        {
911
            docid = manager.getLocalId(guid.getValue());
912
        }
913
        catch(McdbDocNotFoundException mnfe)
914
        {
915
            throw new InvalidRequest("1322", "GUID " + guid + " not found.");
916
        }
917
        
918
        try
919
        {
920
            DocumentImpl.delete(docid, sessionData.getUserName(), sessionData.getGroupNames(), null);
921
        }
922
        catch(Exception e)
923
        {
924
            throw new ServiceFailure("1350", "Could not delete document: " + e.getMessage());
925
        }
926
        
927
        return guid;
928
    }
929

    
930
    /**
931
     * describe a document.  
932
     */
933
    public DescribeResponse describe(AuthToken token, Identifier guid)
934
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
935
            NotImplemented, InvalidRequest {
936
        logCrud.info("describe");
937
        
938
        if(token == null)
939
        {
940
            throw new InvalidToken("1370", "Authentication token is null");
941
        }
942
        
943
        if(guid == null || guid.getValue().trim().equals(""))
944
        {
945
            throw new InvalidRequest("1362", "Guid is null.  A valid guid is required.");
946
        }
947
        
948
        SystemMetadata sm = getSystemMetadata(token, guid);
949
        DescribeResponse dr = new DescribeResponse(sm.getObjectFormat(), 
950
                sm.getSize(), sm.getDateSysMetadataModified(), sm.getChecksum());
951
        return dr;
952
    }
953
    
954
    /**
955
     * get a document with a specified guid.
956
     */
957
    public InputStream get(AuthToken token, Identifier guid)
958
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
959
            NotImplemented {
960
        
961
        // Retrieve the session information from the AuthToken
962
        // If the session is expired, then the user is 'public'
963
        if(token == null)
964
        {
965
            token = new AuthToken("Public");
966
        }
967
        final SessionData sessionData = getSessionData(token);
968
        
969
        // Look up the localId for this global identifier
970
        IdentifierManager im = IdentifierManager.getInstance();
971
        try {
972
            final String localId = im.getLocalId(guid.getValue());
973

    
974
            final InputStreamFromOutputStream<String> objectStream = 
975
                new InputStreamFromOutputStream<String>() {
976
                
977
                @Override
978
                public String produce(final OutputStream dataSink) throws Exception {
979

    
980
                    try {
981
                        String username = "public";
982
                        String[] groups = new String[0];
983
                        if(sessionData != null)
984
                        {
985
                            username = sessionData.getUserName();
986
                            groups = sessionData.getGroupNames();
987
                        }
988
                        /*System.out.println("metacatUrl: " + metacatUrl + 
989
                            " dataSink: " + dataSink + " localId: " + localId + 
990
                            " username: " + username + " params: " + params.toString());
991
                        */    
992
                        /* TODO:
993
                         * This multithreaded approach to getting data without
994
                         * being memory bound causes problems with exception 
995
                         * handling.  The only exception the produce method
996
                         * will return is an IOException, rendering all of the 
997
                         * catch blocks below mute.  This should probably be changed
998
                         * to use a memory mapped solution instead so that we
999
                         * can properly pass exceptions to the client.
1000
                         * see https://trac.dataone.org/ticket/706
1001
                         */
1002
                        handler.readFromMetacat(metacatUrl, null, 
1003
                                dataSink, localId, "xml",
1004
                                username, 
1005
                                groups, true, params);
1006
                    } catch (PropertyNotFoundException e) {
1007
                        e.printStackTrace();
1008
                        throw new ServiceFailure("1030", "Error getting property from metacat: " + e.getMessage());
1009
                    } catch (ClassNotFoundException e) {
1010
                        e.printStackTrace();
1011
                        throw new ServiceFailure("1030", "Class not found error when reading from metacat: " + e.getMessage());
1012
                    } catch (IOException e) {
1013
                        e.printStackTrace();
1014
                        throw new ServiceFailure("1030", "IOException while reading from metacat: " + e.getMessage());
1015
                    } catch (SQLException e) {
1016
                        e.printStackTrace();
1017
                        throw new ServiceFailure("1030", "SQLException while reading from metacat: " + e.getMessage());
1018
                    } catch (McdbException e) {
1019
                        e.printStackTrace();
1020
                        throw new ServiceFailure("1030", "Metacat DB exception while reading from metacat: " + e.getMessage());
1021
                    } catch (ParseLSIDException e) {
1022
                        e.printStackTrace();
1023
                        throw new NotFound("1020", "LSID parsing exception while reading from metacat: " + e.getMessage());
1024
                    } catch (InsufficientKarmaException e) {
1025
                        e.printStackTrace();
1026
                        throw new NotAuthorized("1000", "User not authorized for get(): " + e.getMessage());
1027
                    }
1028

    
1029
                    return "Completed";
1030
                }
1031
            };
1032
            String username = "public";
1033
            if(sessionData != null)
1034
            {
1035
                username = sessionData.getUserName();
1036
            }
1037
            
1038
            EventLog.getInstance().log(metacatUrl,
1039
                    username, im.getLocalId(guid.getValue()), "read");
1040
            logCrud.info("get D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
1041
                    ":");
1042
            return objectStream;
1043

    
1044
        } catch (McdbDocNotFoundException e) {
1045
            throw new NotFound("1020", e.getMessage());
1046
        } 
1047
    }
1048

    
1049
    /**
1050
     * get the checksum for a document.  defaults to MD5.
1051
     */
1052
    public Checksum getChecksum(AuthToken token, Identifier guid)
1053
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1054
            InvalidRequest, NotImplemented 
1055
    {
1056
        logCrud.info("getChecksum");
1057
        return getChecksum(token, guid, "MD5");
1058
    }
1059

    
1060
    /**
1061
     * get the checksum for a document with the given algorithm
1062
     */
1063
    public Checksum getChecksum(AuthToken token, Identifier guid, 
1064
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
1065
            NotAuthorized, NotFound, InvalidRequest, NotImplemented 
1066
    {
1067
        logCrud.info("getChecksum");
1068
        if(checksumAlgorithm == null)
1069
        {
1070
            checksumAlgorithm = "MD5";
1071
        }
1072
        InputStream docStream = get(token, guid);
1073
        String checksum;
1074
        try
1075
        {
1076
            checksum = checksum(docStream, checksumAlgorithm);
1077
        }
1078
        catch(Exception e)
1079
        {
1080
            throw new ServiceFailure("1410", "Error getting checksum: " + e.getMessage());
1081
        }
1082
        Checksum c = new Checksum();
1083
        c.setAlgorithm(ChecksumAlgorithm.convert(checksumAlgorithm));
1084
        c.setValue(checksum);
1085
        return c;
1086
    }
1087

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

    
1231
    /**
1232
     * get the system metadata for a document with a specified guid.
1233
     */
1234
public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
1235
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1236
            InvalidRequest, NotImplemented {
1237
        
1238
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
1239
        
1240
        // Retrieve the session information from the AuthToken
1241
        // If the session is expired, then the user is 'public'
1242
        final SessionData sessionData = getSessionData(token);
1243
                
1244
        try {
1245
            IdentifierManager im = IdentifierManager.getInstance();
1246
            final String localId = im.getSystemMetadataLocalId(guid.getValue());
1247
            
1248
            // Read system metadata from metacat's db
1249
            final InputStreamFromOutputStream<String> objectStream = 
1250
                new InputStreamFromOutputStream<String>() {
1251
                
1252
                @Override
1253
                public String produce(final OutputStream dataSink) throws Exception {
1254
                    //TODO: change to memory mapped IO so that exceptions get 
1255
                    //passed to the response correctly.
1256
                    try {
1257
                        String username = "public";
1258
                        String[] groupnames = null;
1259
                        if(sessionData != null)
1260
                        {
1261
                            username = sessionData.getUserName();
1262
                            groupnames = sessionData.getGroupNames();
1263
                        }
1264
                        
1265
                        handler.readFromMetacat(metacatUrl, null, 
1266
                                dataSink, localId, "xml",
1267
                                username, 
1268
                                groupnames, true, params);
1269
                    } catch (PropertyNotFoundException e) {
1270
                        e.printStackTrace();
1271
                        throw new ServiceFailure("1090", "Property not found while reading system metadata from metacat: " + e.getMessage());
1272
                    } catch (ClassNotFoundException e) {
1273
                        e.printStackTrace();
1274
                        throw new ServiceFailure("1090", "Class not found while reading system metadata from metacat: " + e.getMessage());
1275
                    } catch (IOException e) {
1276
                        e.printStackTrace();
1277
                        throw new ServiceFailure("1090", "IOException while reading system metadata from metacat: " + e.getMessage());
1278
                    } catch (SQLException e) {
1279
                        e.printStackTrace();
1280
                        throw new ServiceFailure("1090", "SQLException while reading system metadata from metacat: " + e.getMessage());
1281
                    } catch (McdbException e) {
1282
                        e.printStackTrace();
1283
                        throw new ServiceFailure("1090", "Metacat DB Exception while reading system metadata from metacat: " + e.getMessage());
1284
                    } catch (ParseLSIDException e) {
1285
                        e.printStackTrace();
1286
                        throw new NotFound("1060", "Error parsing LSID while reading system metadata from metacat: " + e.getMessage());
1287
                    } catch (InsufficientKarmaException e) {
1288
                        e.printStackTrace();
1289
                        throw new NotAuthorized("1040", "User not authorized for get() on system metadata: " + e.getMessage());
1290
                    }
1291

    
1292
                    return "Completed";
1293
                }
1294
            };
1295
            
1296
            // Deserialize the xml to create a SystemMetadata object
1297
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
1298
            String username = "public";
1299
            if(sessionData != null)
1300
            {
1301
                username = sessionData.getUserName();
1302
            }
1303
            EventLog.getInstance().log(metacatUrl,
1304
                    username, im.getLocalId(guid.getValue()), "read");
1305
            logCrud.info("getsystemmetadata D1GUID:" + guid.getValue()  + 
1306
                    ":D1SYSMETADATA:"+ localId + ":");
1307
            return sysmeta;
1308
            
1309
        } catch (McdbDocNotFoundException e) {
1310
            //e.printStackTrace();
1311
            throw new NotFound("1040", e.getMessage());
1312
        }                
1313
    }
1314
    
1315
    /**
1316
     * parse the date in the systemMetadata
1317
     * @param s
1318
     * @return
1319
     * @throws Exception
1320
     */
1321
    public Date parseDate(String s)
1322
      throws Exception
1323
    {
1324
        /* TODO:
1325
         * This method should be replaced by a DateFormatter
1326
         */
1327
        Date d = null;
1328
        int tIndex = s.indexOf("T");
1329
        int zIndex = s.indexOf("Z");
1330
        if(tIndex != -1 && zIndex != -1)
1331
        { //parse a date that looks like 2010-05-18T21:12:54.362Z
1332
            //System.out.println("original date: " + s);
1333
            
1334
            String date = s.substring(0, tIndex);
1335
            String year = date.substring(0, date.indexOf("-"));
1336
            String month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
1337
            String day = date.substring(date.lastIndexOf("-") + 1, date.length());
1338
            /*System.out.println("date: " + "year: " + new Integer(year).intValue() + 
1339
                    " month: " + new Integer(month).intValue() + " day: " + 
1340
                    new Integer(day).intValue());
1341
            */
1342
            String time = s.substring(tIndex + 1, zIndex);
1343
            String hour = time.substring(0, time.indexOf(":"));
1344
            String minute = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
1345
            String seconds = "00";
1346
            String milliseconds = "00";
1347
            if(time.indexOf(".") != -1)
1348
            {
1349
                seconds = time.substring(time.lastIndexOf(":") + 1, time.indexOf("."));
1350
                milliseconds = time.substring(time.indexOf(".") + 1, time.length());
1351
            }
1352
            else
1353
            {
1354
                seconds = time.substring(time.lastIndexOf(":") + 1, time.length());
1355
            }
1356
            /*System.out.println("time: " + "hour: " + new Integer(hour).intValue() + 
1357
                    " minute: " + new Integer(minute).intValue() + " seconds: " + 
1358
                    new Integer(seconds).intValue() + " milli: " + 
1359
                    new Integer(milliseconds).intValue());*/
1360
            
1361
            //d = DateFormat.getDateTimeInstance().parse(date + " " + time);
1362
            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0")/*TimeZone.getDefault()*/);
1363
            c.set(new Integer(year).intValue(), new Integer(month).intValue() - 1, 
1364
                  new Integer(day).intValue(), new Integer(hour).intValue(), 
1365
                  new Integer(minute).intValue(), new Integer(seconds).intValue());
1366
            c.set(Calendar.MILLISECOND, new Integer(milliseconds).intValue());
1367
            d = new Date(c.getTimeInMillis());
1368
            //System.out.println("d: " + d);
1369
            return d;
1370
        }
1371
        else
1372
        {  //if it's not in the expected format, try the formatter
1373
            return DateFormat.getDateTimeInstance().parse(s);
1374
        }
1375
    }
1376

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

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

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

    
1451
        // generate guid/localId pair for object
1452
        logMetacat.debug("Generating a guid/localId mapping");
1453
        IdentifierManager im = IdentifierManager.getInstance();
1454
        String localId = im.generateLocalId(guid.getValue(), 1);
1455

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

    
1519
    /**
1520
     * write a file to a stream
1521
     * @param dir
1522
     * @param fileName
1523
     * @param data
1524
     * @return
1525
     * @throws ServiceFailure
1526
     */
1527
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
1528
        throws ServiceFailure {
1529
        
1530
        File newFile = new File(dir, fileName);
1531
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
1532

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

    
1554
        return newFile;
1555
    }
1556

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

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

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

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