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 com.gc.iotools.stream.is.InputStreamFromOutputStream;
65

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

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

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

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

    
145
        handler = new MetacatHandler(new Timer());
146

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

    
273
        logMetacat.debug("Starting CrudService.create()...");
274
        
275
        // authenticate & get user info
276
        SessionData sessionData = getSessionData(token);
277
        String username = "public";
278
        String[] groups = null;
279
        if(sessionData != null)
280
        {
281
            username = sessionData.getUserName();
282
            groups = sessionData.getGroupNames();
283
        }
284
        String localId = null;
285

    
286
        if (username == null || username.equals("public"))
287
        {
288
            throw new NotAuthorized("1000", "User " + username + " is not authorized to create content." +
289
                    "  If you are not logged in, please do so and retry the request.");
290
        }
291
        
292
        // verify that guid == SystemMetadata.getIdentifier()
293
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
294
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
295
            throw new InvalidSystemMetadata("1180", 
296
                "GUID in method call does not match GUID in system metadata.");
297
        }
298

    
299
        logMetacat.debug("Checking if identifier exists...");
300
        // Check that the identifier does not already exist
301
        IdentifierManager im = IdentifierManager.getInstance();
302
        if (im.identifierExists(guid.getValue())) {
303
            throw new IdentifierNotUnique("1120", 
304
                "GUID is already in use by an existing object.");
305
        }
306

    
307
        // Check if we are handling metadata or data
308
        boolean isScienceMetadata = isScienceMetadata(sysmeta);
309
        
310
        if (isScienceMetadata) {
311
            // CASE METADATA:
312
            try {
313
                this.insertDocument(object, guid, sessionData);
314
                localId = im.getLocalId(guid.getValue());
315
            } catch (IOException e) {
316
                String msg = "Could not create string from XML stream: " +
317
                    " " + e.getMessage();
318
                logMetacat.debug(msg);
319
                throw new ServiceFailure("1190", msg);
320
            } catch(Exception e) {
321
                String msg = "Unexpected error in CrudService.create: " + e.getMessage();
322
                logMetacat.debug(msg);
323
                throw new ServiceFailure("1190", msg);
324
            }
325
            
326

    
327
        } else {
328
            // DEFAULT CASE: DATA (needs to be checked and completed)
329
            insertDataObject(object, guid, sessionData);
330
            
331
        }
332

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

    
790
    /**
791
     * Delete a document.  NOT IMPLEMENTED
792
     */
793
    public Identifier delete(AuthToken token, Identifier guid)
794
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
795
            NotImplemented {
796
        logCrud.info("delete");
797
        throw new NotImplemented("1000", "This method not yet implemented.");
798
    }
799

    
800
    /**
801
     * describe a document.  NOT IMPLEMENTED
802
     */
803
    public DescribeResponse describe(AuthToken token, Identifier guid)
804
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
805
            NotImplemented {
806
        logCrud.info("describe");
807
        throw new NotImplemented("1000", "This method not yet implemented.");
808
    }
809
    
810
    /**
811
     * get a document with a specified guid.
812
     */
813
    public InputStream get(AuthToken token, Identifier guid)
814
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
815
            NotImplemented {
816
        
817
        // Retrieve the session information from the AuthToken
818
        // If the session is expired, then the user is 'public'
819
        if(token == null)
820
        {
821
            token = new AuthToken("Public");
822
        }
823
        final SessionData sessionData = getSessionData(token);
824
        
825
        // Look up the localId for this global identifier
826
        IdentifierManager im = IdentifierManager.getInstance();
827
        try {
828
            final String localId = im.getLocalId(guid.getValue());
829

    
830
            final InputStreamFromOutputStream<String> objectStream = 
831
                new InputStreamFromOutputStream<String>() {
832
                
833
                @Override
834
                public String produce(final OutputStream dataSink) throws Exception {
835

    
836
                    try {
837
                        String username = "public";
838
                        String[] groups = new String[0];
839
                        if(sessionData != null)
840
                        {
841
                            username = sessionData.getUserName();
842
                            groups = sessionData.getGroupNames();
843
                        }
844
                        /*System.out.println("metacatUrl: " + metacatUrl + 
845
                            " dataSink: " + dataSink + " localId: " + localId + 
846
                            " username: " + username + " params: " + params.toString());
847
                        */    
848
                        handler.readFromMetacat(metacatUrl, null, 
849
                                dataSink, localId, "xml",
850
                                username, 
851
                                groups, true, params);
852
                    } catch (PropertyNotFoundException e) {
853
                        e.printStackTrace();
854
                        throw new ServiceFailure("1030", "Error getting property from metacat: " + e.getMessage());
855
                    } catch (ClassNotFoundException e) {
856
                        e.printStackTrace();
857
                        throw new ServiceFailure("1030", "Class not found error when reading from metacat: " + e.getMessage());
858
                    } catch (IOException e) {
859
                        e.printStackTrace();
860
                        throw new ServiceFailure("1030", "IOException while reading from metacat: " + e.getMessage());
861
                    } catch (SQLException e) {
862
                        e.printStackTrace();
863
                        throw new ServiceFailure("1030", "SQLException while reading from metacat: " + e.getMessage());
864
                    } catch (McdbException e) {
865
                        e.printStackTrace();
866
                        throw new ServiceFailure("1030", "Metacat DB exception while reading from metacat: " + e.getMessage());
867
                    } catch (ParseLSIDException e) {
868
                        e.printStackTrace();
869
                        throw new NotFound("1020", "LSID parsing exception while reading from metacat: " + e.getMessage());
870
                    } catch (InsufficientKarmaException e) {
871
                        e.printStackTrace();
872
                        throw new NotAuthorized("1000", "User not authorized for get(): " + e.getMessage());
873
                    }
874

    
875
                    return "Completed";
876
                }
877
            };
878
            String username = "public";
879
            if(sessionData != null)
880
            {
881
                username = sessionData.getUserName();
882
            }
883
            
884
            EventLog.getInstance().log(metacatUrl,
885
                    username, im.getLocalId(guid.getValue()), "read");
886
            logCrud.info("get localId:" + localId + " guid:" + guid.getValue());
887
            return objectStream;
888

    
889
        } catch (McdbDocNotFoundException e) {
890
            throw new NotFound("1020", e.getMessage());
891
        }
892
    }
893

    
894
    /**
895
     * get the checksum for a document.  NOT IMPLEMENTED
896
     */
897
    public Checksum getChecksum(AuthToken token, Identifier guid)
898
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
899
            InvalidRequest, NotImplemented {
900
        logCrud.info("getChecksum");
901
        throw new NotImplemented("1000", "This method not yet implemented.");
902
    }
903

    
904
    /**
905
     * get the checksum for a document.  NOT IMPLEMENTED
906
     */
907
    public Checksum getChecksum(AuthToken token, Identifier guid, 
908
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
909
            NotAuthorized, NotFound, InvalidRequest, NotImplemented {
910
        logCrud.info("getChecksum");
911
        throw new NotImplemented("1000", "This method not yet implemented.");
912
    }
913

    
914
    /**
915
     * get log records.  
916
     */
917
    public Log getLogRecords(AuthToken token, Date fromDate, Date toDate, Event event)
918
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, 
919
            NotImplemented 
920
    {
921
        System.out.println("=================== Getting log records ===================");
922
        System.out.println("Current server time is: " + new Date());
923
        if(fromDate != null)
924
        {
925
          System.out.println("query start time is " + fromDate);
926
        }
927
        if(toDate != null)
928
        {
929
          System.out.println("query end time is " + toDate);
930
        }
931
        Log log = new Log();
932
        Vector<LogEntry> logs = new Vector<LogEntry>();
933
        IdentifierManager im = IdentifierManager.getInstance();
934
        EventLog el = EventLog.getInstance();
935
        if(fromDate == null)
936
        {
937
            System.out.println("setting fromdate from null");
938
            fromDate = new Date(1);
939
        }
940
        if(toDate == null)
941
        {
942
            System.out.println("setting todate from null");
943
            toDate = new Date();
944
        }
945
        
946
        System.out.println("fromDate: " + fromDate);
947
        System.out.println("toDate: " + toDate);
948
        
949
        String report = el.getReport(null, null, null, null, 
950
                new java.sql.Timestamp(fromDate.getTime()), 
951
                new java.sql.Timestamp(toDate.getTime()));
952
        
953
        //System.out.println("report: " + report);
954
        
955
        String logEntry = "<logEntry>";
956
        String endLogEntry = "</logEntry>";
957
        int startIndex = 0;
958
        int foundIndex = report.indexOf(logEntry, startIndex);
959
        while(foundIndex != -1)
960
        {
961
            //parse out each entry
962
            int endEntryIndex = report.indexOf(endLogEntry, foundIndex);
963
            String entry = report.substring(foundIndex, endEntryIndex);
964
            //System.out.println("entry: " + entry);
965
            startIndex = endEntryIndex + endLogEntry.length();
966
            foundIndex = report.indexOf(logEntry, startIndex);
967
            
968
            String entryId = getLogEntryField("entryid", entry);
969
            String ipAddress = getLogEntryField("ipAddress", entry);
970
            String principal = getLogEntryField("principal", entry);
971
            String docid = getLogEntryField("docid", entry);
972
            String eventS = getLogEntryField("event", entry);
973
            String dateLogged = getLogEntryField("dateLogged", entry);
974
            
975
            LogEntry le = new LogEntry();
976
            
977
            Event e = Event.convert(eventS);
978
            if(e == null)
979
            { //skip any events that are not Dataone Crud events
980
                continue;
981
            }
982
            le.setEvent(e);
983
            Identifier entryid = new Identifier();
984
            entryid.setValue(entryId);
985
            le.setEntryId(entryid);
986
            Identifier identifier = new Identifier();
987
            try
988
            {
989
                //System.out.println("converting docid '" + docid + "' to a guid.");
990
                if(docid == null || docid.trim().equals("") || docid.trim().equals("null"))
991
                {
992
                    continue;
993
                }
994
                docid = docid.substring(0, docid.lastIndexOf("."));
995
                identifier.setValue(im.getGUID(docid, im.getLatestRevForLocalId(docid)));
996
            }
997
            catch(Exception ex)
998
            { //try to get the guid, if that doesn't work, just use the local id
999
                //throw new ServiceFailure("1030", "Error getting guid for localId " + 
1000
                //        docid + ": " + ex.getMessage());\
1001
                
1002
                //skip it if the guid can't be found
1003
                continue;
1004
            }
1005
            
1006
            le.setIdentifier(identifier);
1007
            le.setIpAddress(ipAddress);
1008
            Calendar c = Calendar.getInstance();
1009
            String year = dateLogged.substring(0, 4);
1010
            String month = dateLogged.substring(5, 7);
1011
            String date = dateLogged.substring(8, 10);
1012
            //System.out.println("year: " + year + " month: " + month + " day: " + date);
1013
            c.set(new Integer(year).intValue(), new Integer(month).intValue(), new Integer(date).intValue());
1014
            Date logDate = c.getTime();
1015
            le.setDateLogged(logDate);
1016
            NodeReference memberNode = new NodeReference();
1017
            memberNode.setValue(ipAddress);
1018
            le.setMemberNode(memberNode);
1019
            Principal princ = new Principal();
1020
            princ.setValue(principal);
1021
            le.setPrincipal(princ);
1022
            le.setUserAgent("metacat/RESTService");
1023
            
1024
            if(event == null)
1025
            {
1026
                logs.add(le);
1027
            }
1028
            
1029
            if(event != null &&
1030
               e.toString().toLowerCase().trim().equals(event.toString().toLowerCase().trim()))
1031
            {
1032
              logs.add(le);
1033
            }
1034
        }
1035
        
1036
        log.setLogEntryList(logs);
1037
        logCrud.info("getLogRecords");
1038
        return log;
1039
    }
1040
    
1041
    /**
1042
     * parse a logEntry and get the relavent field from it
1043
     * @param fieldname
1044
     * @param entry
1045
     * @return
1046
     */
1047
    private String getLogEntryField(String fieldname, String entry)
1048
    {
1049
        String begin = "<" + fieldname + ">";
1050
        String end = "</" + fieldname + ">";
1051
        //System.out.println("looking for " + begin + " and " + end + " in entry " + entry);
1052
        String s = entry.substring(entry.indexOf(begin) + begin.length(), entry.indexOf(end));
1053
        //System.out.println("entry " + fieldname + " : " + s);
1054
        return s;
1055
    }
1056

    
1057
    /**
1058
     * get the system metadata for a document with a specified guid.
1059
     */
1060
    public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
1061
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1062
            InvalidRequest, NotImplemented {
1063
        
1064
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
1065
        
1066
        // Retrieve the session information from the AuthToken
1067
        // If the session is expired, then the user is 'public'
1068
        final SessionData sessionData = getSessionData(token);
1069
                
1070
        try {
1071
            IdentifierManager im = IdentifierManager.getInstance();
1072
            final String localId = im.getSystemMetadataLocalId(guid.getValue());
1073
            
1074
            // Read system metadata from metacat's db
1075
            final InputStreamFromOutputStream<String> objectStream = 
1076
                new InputStreamFromOutputStream<String>() {
1077
                
1078
                @Override
1079
                public String produce(final OutputStream dataSink) throws Exception {
1080
                    try {
1081
                        handler.readFromMetacat(metacatUrl, null, 
1082
                                dataSink, localId, "xml",
1083
                                sessionData.getUserName(), 
1084
                                sessionData.getGroupNames(), true, params);
1085
                    } catch (PropertyNotFoundException e) {
1086
                        e.printStackTrace();
1087
                        throw new ServiceFailure("1030", "Property not found while reading system metadata from metacat: " + e.getMessage());
1088
                    } catch (ClassNotFoundException e) {
1089
                        e.printStackTrace();
1090
                        throw new ServiceFailure("1030", "Class not found while reading system metadata from metacat: " + e.getMessage());
1091
                    } catch (IOException e) {
1092
                        e.printStackTrace();
1093
                        throw new ServiceFailure("1030", "IOException while reading system metadata from metacat: " + e.getMessage());
1094
                    } catch (SQLException e) {
1095
                        e.printStackTrace();
1096
                        throw new ServiceFailure("1030", "SQLException while reading system metadata from metacat: " + e.getMessage());
1097
                    } catch (McdbException e) {
1098
                        e.printStackTrace();
1099
                        throw new ServiceFailure("1030", "Metacat DB Exception while reading system metadata from metacat: " + e.getMessage());
1100
                    } catch (ParseLSIDException e) {
1101
                        e.printStackTrace();
1102
                        throw new NotFound("1020", "Error parsing LSID while reading system metadata from metacat: " + e.getMessage());
1103
                    } catch (InsufficientKarmaException e) {
1104
                        e.printStackTrace();
1105
                        throw new NotAuthorized("1000", "User not authorized for get() on system metadata: " + e.getMessage());
1106
                    }
1107

    
1108
                    return "Completed";
1109
                }
1110
            };
1111
            
1112
            // Deserialize the xml to create a SystemMetadata object
1113
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
1114
            String username = "public";
1115
            if(sessionData != null)
1116
            {
1117
                username = sessionData.getUserName();
1118
            }
1119
            EventLog.getInstance().log(metacatUrl,
1120
                    username, im.getLocalId(guid.getValue()), "read");
1121
            logCrud.info("getSystemMetadata localId: " + localId + " guid:" + guid.getValue());
1122
            return sysmeta;
1123
            
1124
        } catch (McdbDocNotFoundException e) {
1125
            //e.printStackTrace();
1126
            throw new NotFound("1000", e.getMessage());
1127
        }                
1128
    }
1129
    
1130
    /**
1131
     * parse the date in the systemMetadata
1132
     * @param s
1133
     * @return
1134
     * @throws Exception
1135
     */
1136
    public Date parseDate(String s)
1137
      throws Exception
1138
    {
1139
        Date d = null;
1140
        int tIndex = s.indexOf("T");
1141
        int zIndex = s.indexOf("Z");
1142
        if(tIndex != -1 && zIndex != -1)
1143
        { //parse a date that looks like 2010-05-18T21:12:54.362Z
1144
            //System.out.println("original date: " + s);
1145
            
1146
            String date = s.substring(0, tIndex);
1147
            String year = date.substring(0, date.indexOf("-"));
1148
            String month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
1149
            String day = date.substring(date.lastIndexOf("-") + 1, date.length());
1150
            /*System.out.println("date: " + "year: " + new Integer(year).intValue() + 
1151
                    " month: " + new Integer(month).intValue() + " day: " + 
1152
                    new Integer(day).intValue());
1153
            */
1154
            String time = s.substring(tIndex + 1, zIndex);
1155
            String hour = time.substring(0, time.indexOf(":"));
1156
            String minute = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
1157
            String seconds = "00";
1158
            String milliseconds = "00";
1159
            if(time.indexOf(".") != -1)
1160
            {
1161
                seconds = time.substring(time.lastIndexOf(":") + 1, time.indexOf("."));
1162
                milliseconds = time.substring(time.indexOf(".") + 1, time.length());
1163
            }
1164
            else
1165
            {
1166
                seconds = time.substring(time.lastIndexOf(":") + 1, time.length());
1167
            }
1168
            /*System.out.println("time: " + "hour: " + new Integer(hour).intValue() + 
1169
                    " minute: " + new Integer(minute).intValue() + " seconds: " + 
1170
                    new Integer(seconds).intValue() + " milli: " + 
1171
                    new Integer(milliseconds).intValue());*/
1172
            
1173
            //d = DateFormat.getDateTimeInstance().parse(date + " " + time);
1174
            Calendar c = Calendar.getInstance(/*TimeZone.getTimeZone("GMT-0")*/TimeZone.getDefault());
1175
            c.set(new Integer(year).intValue(), new Integer(month).intValue() - 1, 
1176
                  new Integer(day).intValue(), new Integer(hour).intValue(), 
1177
                  new Integer(minute).intValue(), new Integer(seconds).intValue());
1178
            c.set(Calendar.MILLISECOND, new Integer(milliseconds).intValue());
1179
            d = new Date(c.getTimeInMillis());
1180
            //System.out.println("d: " + d);
1181
            return d;
1182
        }
1183
        else
1184
        {  //if it's not in the expected format, try the formatter
1185
            return DateFormat.getDateTimeInstance().parse(s);
1186
        }
1187
    }
1188

    
1189
    /*
1190
     * Look up the information on the session using the token provided in
1191
     * the AuthToken.  The Session should have all relevant user information.
1192
     * If the session has expired or is invalid, the 'public' session will
1193
     * be returned, giving the user anonymous access.
1194
     */
1195
    public static SessionData getSessionData(AuthToken token) {
1196
        SessionData sessionData = null;
1197
        String sessionId = "PUBLIC";
1198
        if (token != null) {
1199
            sessionId = token.getToken();
1200
        }
1201
        
1202
        // if the session id is registered in SessionService, get the
1203
        // SessionData for it. Otherwise, use the public session.
1204
        //System.out.println("sessionid: " + sessionId);
1205
        if (sessionId != null &&
1206
            !sessionId.toLowerCase().equals("public") &&
1207
            SessionService.getInstance().isSessionRegistered(sessionId)) 
1208
        {
1209
            sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
1210
        } else {
1211
            sessionData = SessionService.getInstance().getPublicSession();
1212
        }
1213
        
1214
        return sessionData;
1215
    }
1216

    
1217
    /** 
1218
     * Determine if a given object should be treated as an XML science metadata
1219
     * object. 
1220
     * 
1221
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
1222
     * 
1223
     * @param sysmeta the SystemMetadata describig the object
1224
     * @return true if the object should be treated as science metadata
1225
     */
1226
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
1227
        boolean scimeta = false;
1228
        switch (sysmeta.getObjectFormat()) {
1229
            case EML_2_1_0: scimeta = true; break;
1230
            case EML_2_0_1: scimeta = true; break;
1231
            case EML_2_0_0: scimeta = true; break;
1232
            case FGDC_STD_001_1_1999: scimeta = true; break;
1233
            case FGDC_STD_001_1998: scimeta = true; break;
1234
            case NCML_2_2: scimeta = true; break;
1235
        }
1236
        
1237
        return scimeta;
1238
    }
1239

    
1240
    /**
1241
     * insert a data doc
1242
     * @param object
1243
     * @param guid
1244
     * @param sessionData
1245
     * @throws ServiceFailure
1246
     */
1247
    private void insertDataObject(InputStream object, Identifier guid, 
1248
            SessionData sessionData) throws ServiceFailure {
1249
        
1250
        String username = "public";
1251
        String[] groups = null;
1252
        if(sessionData != null)
1253
        {
1254
          username = sessionData.getUserName();
1255
          groups = sessionData.getGroupNames();
1256
        }
1257

    
1258
        // generate guid/localId pair for object
1259
        logMetacat.debug("Generating a guid/localId mapping");
1260
        IdentifierManager im = IdentifierManager.getInstance();
1261
        String localId = im.generateLocalId(guid.getValue(), 1);
1262

    
1263
        try {
1264
            logMetacat.debug("Case DATA: starting to write to disk.");
1265
            if (DocumentImpl.getDataFileLockGrant(localId)) {
1266
    
1267
                // Save the data file to disk using "localId" as the name
1268
                try {
1269
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
1270
    
1271
                    File dataDirectory = new File(datafilepath);
1272
                    dataDirectory.mkdirs();
1273
    
1274
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
1275
    
1276
                    // TODO: Check that the file size matches SystemMetadata
1277
                    //                        long size = newFile.length();
1278
                    //                        if (size == 0) {
1279
                    //                            throw new IOException("Uploaded file is 0 bytes!");
1280
                    //                        }
1281
    
1282
                    // Register the file in the database (which generates an exception
1283
                    // if the localId is not acceptable or other untoward things happen
1284
                    try {
1285
                        logMetacat.debug("Registering document...");
1286
                        DocumentImpl.registerDocument(localId, "BIN", localId,
1287
                                username, groups);
1288
                        logMetacat.debug("Registration step completed.");
1289
                    } catch (SQLException e) {
1290
                        //newFile.delete();
1291
                        logMetacat.debug("SQLE: " + e.getMessage());
1292
                        e.printStackTrace(System.out);
1293
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1294
                    } catch (AccessionNumberException e) {
1295
                        //newFile.delete();
1296
                        logMetacat.debug("ANE: " + e.getMessage());
1297
                        e.printStackTrace(System.out);
1298
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1299
                    } catch (Exception e) {
1300
                        //newFile.delete();
1301
                        logMetacat.debug("Exception: " + e.getMessage());
1302
                        e.printStackTrace(System.out);
1303
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1304
                    }
1305
    
1306
                    logMetacat.debug("Logging the creation event.");
1307
                    EventLog.getInstance().log(metacatUrl,
1308
                            username, localId, "create");
1309
    
1310
                    // Schedule replication for this data file
1311
                    logMetacat.debug("Scheduling replication.");
1312
                    ForceReplicationHandler frh = new ForceReplicationHandler(
1313
                            localId, "create", false, null);
1314
    
1315
                } catch (PropertyNotFoundException e) {
1316
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
1317
                }
1318
    
1319
            }
1320
        } catch (Exception e) {
1321
            // Could not get a lock on the document, so we can not update the file now
1322
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
1323
        }
1324
    }
1325

    
1326
    /**
1327
     * write a file to a stream
1328
     * @param dir
1329
     * @param fileName
1330
     * @param data
1331
     * @return
1332
     * @throws ServiceFailure
1333
     */
1334
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
1335
        throws ServiceFailure {
1336
        
1337
        File newFile = new File(dir, fileName);
1338
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
1339

    
1340
        try {
1341
            if (newFile.createNewFile()) {
1342
                // write data stream to desired file
1343
                OutputStream os = new FileOutputStream(newFile);
1344
                long length = IOUtils.copyLarge(data, os);
1345
                os.flush();
1346
                os.close();
1347
            } else {
1348
                logMetacat.debug("File creation failed, or file already exists.");
1349
                throw new ServiceFailure("1190", "File already exists: " + fileName);
1350
            }
1351
        } catch (FileNotFoundException e) {
1352
            logMetacat.debug("FNF: " + e.getMessage());
1353
            throw new ServiceFailure("1190", "File not found: " + fileName + " " 
1354
                    + e.getMessage());
1355
        } catch (IOException e) {
1356
            logMetacat.debug("IOE: " + e.getMessage());
1357
            throw new ServiceFailure("1190", "File was not written: " + fileName 
1358
                    + " " + e.getMessage());
1359
        }
1360

    
1361
        return newFile;
1362
    }
1363
    
1364
    private static Date getDateInTimeZone(Date currentDate, String timeZoneId)
1365
    {
1366
        TimeZone tz = TimeZone.getTimeZone(timeZoneId);
1367
        Calendar mbCal = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId));
1368
        mbCal.setTimeInMillis(currentDate.getTime());
1369

    
1370
        Calendar cal = Calendar.getInstance();
1371
        cal.set(Calendar.YEAR, mbCal.get(Calendar.YEAR));
1372
        cal.set(Calendar.MONTH, mbCal.get(Calendar.MONTH));
1373
        cal.set(Calendar.DAY_OF_MONTH, mbCal.get(Calendar.DAY_OF_MONTH));
1374
        cal.set(Calendar.HOUR_OF_DAY, mbCal.get(Calendar.HOUR_OF_DAY));
1375
        cal.set(Calendar.MINUTE, mbCal.get(Calendar.MINUTE));
1376
        cal.set(Calendar.SECOND, mbCal.get(Calendar.SECOND));
1377
        cal.set(Calendar.MILLISECOND, mbCal.get(Calendar.MILLISECOND));
1378

    
1379
        return cal.getTime();
1380
    }
1381

    
1382
    /**
1383
     * insert a systemMetadata doc, return the localId of the sysmeta
1384
     */
1385
    private String insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData) 
1386
        throws ServiceFailure 
1387
    {
1388
        logMetacat.debug("Starting to insert SystemMetadata...");
1389
    
1390
        // generate guid/localId pair for sysmeta
1391
        Identifier sysMetaGuid = new Identifier();
1392
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
1393
        sysmeta.setDateSysMetadataModified(new Date());
1394
        System.out.println("****inserting new system metadata with modified date " + sysmeta.getDateSysMetadataModified());
1395

    
1396
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
1397
        System.out.println("sysmeta: " + xml);
1398
        String localId = insertDocument(xml, sysMetaGuid, sessionData);
1399
        System.out.println("sysmeta inserted with localId " + localId);
1400
        //insert the system metadata doc id into the identifiers table to 
1401
        //link it to the data or metadata document
1402
        IdentifierManager.getInstance().createSystemMetadataMapping(
1403
                sysmeta.getIdentifier().getValue(), sysMetaGuid.getValue());
1404
        return localId;
1405
    }
1406
    
1407
    /**
1408
     * update a systemMetadata doc
1409
     */
1410
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
1411
      throws ServiceFailure
1412
    {
1413
        try
1414
        {
1415
            String smId = IdentifierManager.getInstance().getSystemMetadataLocalId(sm.getIdentifier().getValue());
1416
            sm.setDateSysMetadataModified(new Date());
1417
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
1418
            Identifier id = new Identifier();
1419
            id.setValue(smId);
1420
            String localId = updateDocument(xml, id, null, sessionData);
1421
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
1422
        }
1423
        catch(Exception e)
1424
        {
1425
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getMessage());
1426
        }
1427
    }
1428
    
1429
    /**
1430
     * insert a document
1431
     * NOTE: this method shouldn't be used from the update or create() methods.  
1432
     * we shouldn't be putting the science metadata or data objects into memory.
1433
     */
1434
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
1435
        throws ServiceFailure
1436
    {
1437
        return insertOrUpdateDocument(xml, guid, sessionData, "insert");
1438
    }
1439
    
1440
    /**
1441
     * insert a document from a stream
1442
     */
1443
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
1444
      throws IOException, ServiceFailure
1445
    {
1446
        //HACK: change this eventually.  we should not be converting the stream to a string
1447
        String xml = IOUtils.toString(is);
1448
        return insertDocument(xml, guid, sessionData);
1449
    }
1450
    
1451
    /**
1452
     * update a document
1453
     * NOTE: this method shouldn't be used from the update or create() methods.  
1454
     * we shouldn't be putting the science metadata or data objects into memory.
1455
     */
1456
    private String updateDocument(String xml, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
1457
        throws ServiceFailure
1458
    {
1459
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update");
1460
    }
1461
    
1462
    /**
1463
     * update a document from a stream
1464
     */
1465
    private String updateDocument(InputStream is, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
1466
      throws IOException, ServiceFailure
1467
    {
1468
        //HACK: change this eventually.  we should not be converting the stream to a string
1469
        String xml = IOUtils.toString(is);
1470
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData);
1471
        IdentifierManager im = IdentifierManager.getInstance();
1472
        if(guid != null)
1473
        {
1474
          im.createMapping(guid.getValue(), localId);
1475
        }
1476
        return localId;
1477
    }
1478
    
1479
    /**
1480
     * insert a document, return the id of the document that was inserted
1481
     */
1482
    protected String insertOrUpdateDocument(String xml, Identifier guid, SessionData sessionData, String insertOrUpdate) 
1483
        throws ServiceFailure {
1484
        logMetacat.debug("Starting to insert xml document...");
1485
        IdentifierManager im = IdentifierManager.getInstance();
1486

    
1487
        // generate guid/localId pair for sysmeta
1488
        String localId = null;
1489
        if(insertOrUpdate.equals("insert"))
1490
        {
1491
            localId = im.generateLocalId(guid.getValue(), 1);
1492
        }
1493
        else
1494
        {
1495
            //localid should already exist in the identifier table, so just find it
1496
            try
1497
            {
1498
                localId = im.getLocalId(guid.getValue());
1499
                //increment the revision
1500
                String docid = localId.substring(0, localId.lastIndexOf("."));
1501
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
1502
                int rev = new Integer(revS).intValue();
1503
                rev++;
1504
                docid = docid + "." + rev;
1505
                localId = docid;
1506
            }
1507
            catch(McdbDocNotFoundException e)
1508
            {
1509
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
1510
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
1511
            }
1512
        }
1513
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
1514
                localId);
1515

    
1516
        String[] action = new String[1];
1517
        action[0] = insertOrUpdate;
1518
        params.put("action", action);
1519
        String[] docid = new String[1];
1520
        docid[0] = localId;
1521
        params.put("docid", docid);
1522
        String[] doctext = new String[1];
1523
        doctext[0] = xml;
1524
        logMetacat.debug(doctext[0]);
1525
        params.put("doctext", doctext);
1526
        
1527
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
1528
        // onto output stream, or alternatively, capture that and parse it to 
1529
        // generate the right exceptions
1530
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
1531
        //PrintWriter pw = new PrintWriter(output);
1532
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
1533
                            null, params, sessionData.getUserName(), sessionData.getGroupNames());
1534
        //String outputS = new String(output.toByteArray());
1535
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
1536
        logMetacat.debug("Finsished inserting xml document with id " + localId);
1537
        return localId;
1538
    }
1539
    
1540
    /**
1541
     * serialize a system metadata doc
1542
     * @param sysmeta
1543
     * @return
1544
     * @throws ServiceFailure
1545
     */
1546
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta) 
1547
        throws ServiceFailure {
1548
        IBindingFactory bfact;
1549
        ByteArrayOutputStream sysmetaOut = null;
1550
        try {
1551
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
1552
            IMarshallingContext mctx = bfact.createMarshallingContext();
1553
            sysmetaOut = new ByteArrayOutputStream();
1554
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
1555
        } catch (JiBXException e) {
1556
            e.printStackTrace();
1557
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
1558
        }
1559
        
1560
        return sysmetaOut;
1561
    }
1562
    
1563
    /**
1564
     * deserialize a system metadata doc
1565
     * @param xml
1566
     * @return
1567
     * @throws ServiceFailure
1568
     */
1569
    public static SystemMetadata deserializeSystemMetadata(InputStream xml) 
1570
        throws ServiceFailure {
1571
        try {
1572
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
1573
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1574
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
1575
            return sysmeta;
1576
        } catch (JiBXException e) {
1577
            e.printStackTrace();
1578
            throw new ServiceFailure("1190", "Failed to deserialize and insert SystemMetadata: " + e.getMessage());
1579
        }    
1580
    }
1581
    
1582
    /**
1583
     * produce an md5 checksum for item
1584
     */
1585
    private String checksum(InputStream is)
1586
      throws Exception
1587
    {        
1588
        byte[] buffer = new byte[1024];
1589
        MessageDigest complete = MessageDigest.getInstance("MD5");
1590
        int numRead;
1591
        
1592
        do 
1593
        {
1594
          numRead = is.read(buffer);
1595
          if (numRead > 0) 
1596
          {
1597
            complete.update(buffer, 0, numRead);
1598
          }
1599
        } while (numRead != -1);
1600
        
1601
        
1602
        return getHex(complete.digest());
1603
    }
1604
    
1605
    /**
1606
     * convert a byte array to a hex string
1607
     */
1608
    private static String getHex( byte [] raw ) 
1609
    {
1610
        final String HEXES = "0123456789ABCDEF";
1611
        if ( raw == null ) {
1612
          return null;
1613
        }
1614
        final StringBuilder hex = new StringBuilder( 2 * raw.length );
1615
        for ( final byte b : raw ) {
1616
          hex.append(HEXES.charAt((b & 0xF0) >> 4))
1617
             .append(HEXES.charAt((b & 0x0F)));
1618
        }
1619
        return hex.toString();
1620
    }
1621
    
1622
    /**
1623
     * parse the metacat date which looks like 2010-06-08 (YYYY-MM-DD) into
1624
     * a proper date object
1625
     * @param date
1626
     * @return
1627
     */
1628
    private Date parseMetacatDate(String date)
1629
    {
1630
        String year = date.substring(0, 4);
1631
        String month = date.substring(5, 7);
1632
        String day = date.substring(8, 10);
1633
        Calendar c = Calendar.getInstance(TimeZone.getDefault());
1634
        c.set(new Integer(year).intValue(), 
1635
              new Integer(month).intValue(), 
1636
              new Integer(day).intValue());
1637
        System.out.println("time in parseMetacatDate: " + c.getTime());
1638
        return c.getTime();
1639
    }
1640
    
1641
    /**
1642
     * find the size (in bytes) of a stream
1643
     * @param is
1644
     * @return
1645
     * @throws IOException
1646
     */
1647
    private long sizeOfStream(InputStream is)
1648
        throws IOException
1649
    {
1650
        long size = 0;
1651
        byte[] b = new byte[1024];
1652
        int numread = is.read(b, 0, 1024);
1653
        while(numread != -1)
1654
        {
1655
            size += numread;
1656
            numread = is.read(b, 0, 1024);
1657
        }
1658
        return size;
1659
    }
1660
    
1661
    /**
1662
     * create system metadata with a specified id, doc and format
1663
     */
1664
    private SystemMetadata createSystemMetadata(String localId, AuthToken token)
1665
      throws Exception
1666
    {
1667
        IdentifierManager im = IdentifierManager.getInstance();
1668
        Hashtable<String, Object> docInfo = im.getDocumentInfo(localId);
1669
        
1670
        //get the document text
1671
        int rev = im.getLatestRevForLocalId(localId);
1672
        Identifier identifier = new Identifier();
1673
        identifier.setValue(im.getGUID(localId, rev));
1674
        InputStream is = this.get(token, identifier);
1675
        
1676
        SystemMetadata sm = new SystemMetadata();
1677
        //set the id
1678
        sm.setIdentifier(identifier);
1679
        
1680
        //set the object format
1681
        String doctype = (String)docInfo.get("doctype");
1682
        ObjectFormat format = ObjectFormat.convert((String)docInfo.get("doctype"));
1683
        if(format == null)
1684
        {
1685
            if(doctype.trim().equals("BIN"))
1686
            {
1687
                format = ObjectFormat.APPLICATIONOCTETSTREAM;
1688
            }
1689
            else
1690
            {
1691
                format = ObjectFormat.convert("text/plain");
1692
            }
1693
        }
1694
        sm.setObjectFormat(format);
1695
        
1696
        //create the checksum
1697
        String checksumS = checksum(is);
1698
        ChecksumAlgorithm ca = ChecksumAlgorithm.convert("MD5");
1699
        Checksum checksum = new Checksum();
1700
        checksum.setValue(checksumS);
1701
        checksum.setAlgorithm(ca);
1702
        sm.setChecksum(checksum);
1703
        
1704
        //set the size
1705
        is = this.get(token, identifier);
1706
        sm.setSize(sizeOfStream(is));
1707
        
1708
        //submitter
1709
        Principal p = new Principal();
1710
        p.setValue((String)docInfo.get("user_owner"));
1711
        sm.setSubmitter(p);
1712
        sm.setRightsHolder(p);
1713
        try
1714
        {
1715
            Date dateCreated = parseMetacatDate((String)docInfo.get("date_created"));
1716
            sm.setDateUploaded(dateCreated);
1717
            Date dateUpdated = parseMetacatDate((String)docInfo.get("date_updated"));
1718
            sm.setDateSysMetadataModified(dateUpdated);
1719
        }
1720
        catch(Exception e)
1721
        {
1722
            System.out.println("POSSIBLE ERROR: couldn't parse a date: " + e.getMessage());
1723
            Date dateCreated = new Date();
1724
            sm.setDateUploaded(dateCreated);
1725
            Date dateUpdated = new Date();
1726
            sm.setDateSysMetadataModified(dateUpdated);
1727
        }
1728
        NodeReference nr = new NodeReference();
1729
        nr.setValue("metacat");
1730
        sm.setOriginMemberNode(nr);
1731
        sm.setAuthoritativeMemberNode(nr);
1732
        return sm;
1733
    }
1734
}
(1-1/2)