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.sql.SQLException;
34
import java.util.Date;
35
import java.util.Enumeration;
36
import java.util.Hashtable;
37
import java.util.Timer;
38
import java.util.List;
39
import java.text.DateFormat;
40

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

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

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

    
67
import edu.ucsb.nceas.metacat.AccessionNumberException;
68
import edu.ucsb.nceas.metacat.MetacatResultSet;
69
import edu.ucsb.nceas.metacat.MetacatResultSet.Document;
70
import edu.ucsb.nceas.metacat.DocumentImpl;
71
import edu.ucsb.nceas.metacat.EventLog;
72
import edu.ucsb.nceas.metacat.IdentifierManager;
73
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
74
import edu.ucsb.nceas.metacat.McdbException;
75
import edu.ucsb.nceas.metacat.MetacatHandler;
76
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
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 ServletContext servletContext;
94
    private HttpServletRequest request;
95
    private HttpServletResponse response;*/
96
    
97
    private static CrudService crudService = null;
98

    
99
    private MetacatHandler handler;
100
    private Hashtable<String, String[]> params;
101
    Logger logMetacat = null;
102
    
103
    private String metacatUrl;
104

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

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

    
149
    }
150
    
151
    /**
152
     * return the context url CrudService is using.
153
     */
154
    public String getContextUrl()
155
    {
156
        return metacatUrl;
157
    }
158
    
159
    /**
160
     * set the params for this service from an HttpServletRequest param list
161
     */
162
    public void setParamsFromRequest(HttpServletRequest request)
163
    {
164
        Enumeration paramlist = request.getParameterNames();
165
        while (paramlist.hasMoreElements()) {
166
            String name = (String) paramlist.nextElement();
167
            String[] value = (String[])request.getParameterValues(name);
168
            params.put(name, value);
169
        }
170
    }
171
    
172
    /**
173
     * set the parameter values needed for this request
174
     */
175
    public void setParameter(String name, String[] value)
176
    {
177
        params.put(name, value);
178
    }
179
    
180
    public Identifier create(AuthToken token, Identifier guid, 
181
            InputStream object, SystemMetadata sysmeta) throws InvalidToken, 
182
            ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType, 
183
            InsufficientResources, InvalidSystemMetadata, NotImplemented {
184

    
185
        logMetacat.debug("Starting CrudService.create()...");
186
        
187
        // authenticate & get user info
188
        SessionData sessionData = getSessionData(token);
189
        String username = sessionData.getUserName();
190
        String[] groups = sessionData.getGroupNames();
191

    
192
        // verify that guid == SystemMetadata.getIdentifier()
193
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
194
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
195
            throw new InvalidSystemMetadata("1180", 
196
                "GUID in method call does not match GUID in system metadata.");
197
        }
198

    
199
        logMetacat.debug("Checking if identifier exists...");
200
        // Check that the identifier does not already exist
201
        IdentifierManager im = IdentifierManager.getInstance();
202
        if (im.identifierExists(guid.getValue())) {
203
            throw new IdentifierNotUnique("1120", 
204
                "GUID is already in use by an existing object.");
205
        }
206

    
207
        // Check if we are handling metadata or data
208
        boolean isScienceMetadata = isScienceMetadata(sysmeta);
209
        
210
        if (isScienceMetadata) {
211
            // CASE METADATA:
212
            try {
213
                this.insertDocument(object, guid, sessionData);
214
            } catch (IOException e) {
215
                String msg = "Could not create string from XML stream: " +
216
                    " " + e.getMessage();
217
                logMetacat.debug(msg);
218
                throw new ServiceFailure("1190", msg);
219
            }
220

    
221
        } else {
222
            // DEFAULT CASE: DATA (needs to be checked and completed)
223
            insertDataObject(object, guid, sessionData);
224
            
225
        }
226

    
227
        // For Metadata and Data, insert the system metadata into the object store too
228
        insertSystemMetadata(sysmeta, sessionData);
229

    
230
        logMetacat.debug("Returning from CrudService.create()");
231
        return guid;
232
    }
233
    
234
    /**
235
     * update an existing object with a new object.  Change the system metadata
236
     * to reflect the changes and update it as well.
237
     */
238
    public Identifier update(AuthToken token, Identifier guid, 
239
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta) 
240
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
241
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
242
            NotImplemented {
243
        try
244
        {
245
            SessionData sessionData = getSessionData(token);
246
            
247
            //find the old systemmetadata (sm.old) document id (the one linked to obsoletedGuid)
248
            SystemMetadata sm = getSystemMetadata(token, obsoletedGuid);
249
            //change sm.old's obsoletedBy field 
250
            List l = sm.getObsoletedByList();
251
            l.add(guid);
252
            sm.setObsoletedByList(l);
253
            //update sm.old
254
            updateSystemMetadata(sm, sessionData);
255
            
256
            //change the obsoletes field of the new systemMetadata (sm.new) to point to the id of the old one
257
            sysmeta.addObsolete(obsoletedGuid);
258
            //insert sm.new
259
            insertSystemMetadata(sysmeta, sessionData);
260
            
261
            boolean isScienceMetadata = isScienceMetadata(sysmeta);
262
            if(isScienceMetadata)
263
            {
264
                //update the doc
265
                updateDocument(object, obsoletedGuid, guid, sessionData);
266
            }
267
            else
268
            {
269
                //update a data file, not xml
270
                insertDataObject(object, guid, sessionData);
271
            }
272
            return guid;
273
        }
274
        catch(Exception e)
275
        {
276
            throw new ServiceFailure("1030", "Error updating document in CrudService: " + e.getMessage());
277
        }
278
    }
279
    
280
    /**
281
     * set the permission on the document
282
     * @param token
283
     * @param principal
284
     * @param permission
285
     * @param permissionType
286
     * @param permissionOrder
287
     * @return
288
     */
289
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
290
            String permissionType, String permissionOrder)
291
      throws ServiceFailure
292
    {
293
        try
294
        {
295
            IdentifierManager im = IdentifierManager.getInstance();
296
            String docid = im.getLocalId(id.getValue());
297
            final SessionData sessionData = getSessionData(token);
298
            String permNum = "0";
299
            if(permission == "read")
300
            {
301
                permNum = "4";
302
            }
303
            else if(permission == "write")
304
            {
305
                permNum = "6";
306
            }
307
            handler.setAccess(metacatUrl, sessionData.getUserName(), docid, 
308
                    principal, permNum, permissionType, permissionOrder);
309
        }
310
        catch(Exception e)
311
        {
312
            throw new ServiceFailure("1000", "Could not set access on the document with id " + id.getValue());
313
        }
314
    }
315
    
316
    /**
317
     *  Retrieve the list of objects present on the MN that match the calling 
318
     *  parameters. This method is required to support the process of Member 
319
     *  Node synchronization. At a minimum, this method should be able to 
320
     *  return a list of objects that match:
321
     *  startTime <= SystemMetadata.dateSysMetadataModified
322
     *  but is expected to also support date range (by also specifying endTime), 
323
     *  and should also support slicing of the matching set of records by 
324
     *  indicating the starting index of the response (where 0 is the index 
325
     *  of the first item) and the count of elements to be returned.
326
     *  
327
     *  If startTime or endTime is null, the query is not restricted by that parameter.
328
     *  
329
     * @see http://mule1.dataone.org/ArchitectureDocs/mn_api_replication.html#MN_replication.listObjects
330
     * @param token
331
     * @param startTime
332
     * @param endTime
333
     * @param objectFormat
334
     * @param replicaStatus
335
     * @param start
336
     * @param count
337
     * @return ObjectList
338
     * @throws NotAuthorized
339
     * @throws InvalidRequest
340
     * @throws NotImplemented
341
     * @throws ServiceFailure
342
     * @throws InvalidToken
343
     */
344
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
345
        ObjectFormat objectFormat, boolean replicaStatus, int start, int count)
346
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
347
    {
348
      ObjectList ol = new ObjectList();
349
      final SessionData sessionData = getSessionData(token);
350
      try
351
      {
352
          params.clear();
353
          params.put("returndoctype", new String[] {"http://dataone.org/service/types/SystemMetadata/0.1"});
354
          params.put("qformat", new String[] {"xml"});
355
          params.put("returnfield", new String[] {"size", "originMemberNode", 
356
                  "identifier", "objectFormat", "dateSysMetadataModified", "checksum"});
357
          params.put("anyfield", new String[] {"%"});
358
          
359
          System.out.println("listing objects");
360
          MetacatResultSet rs = handler.query(metacatUrl, params, sessionData.getUserName(), 
361
                  sessionData.getGroupNames(), sessionData.getId());
362
          List docs = rs.getDocuments();
363
          
364
          for(int i=0; i<docs.size(); i++)
365
          {
366
              //get the document from the results
367
              Document d = (Document)docs.get(i);
368
              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
369
              if(!objectFormat.toString().trim().equals(returnedObjectFormat.toString().trim()))
370
              { //make sure the objectFormat is the one specified
371
                  continue;
372
              }
373
              Date dateSysMetadataModified = DateFormat.getDateTimeInstance().parse(d.getField("dateSysMetadataModified"));
374
              int startDateComparison = 0;
375
              int endDateComparison = 0;
376
              if(startTime != null)
377
              {
378
                  startDateComparison = dateSysMetadataModified.compareTo(startTime);
379
              }
380
              
381
              if(endTime != null)
382
              {
383
                  endDateComparison = dateSysMetadataModified.compareTo(endTime);
384
              }
385
              
386
              if(startDateComparison < 0 || endDateComparison > 0)
387
              { //this date falls outside of the startTime and endTime params, so
388
                //skip it
389
                  continue;                  
390
              }
391
              
392
              ObjectInfo info = new ObjectInfo();
393
              //add the fields to the info object
394
              Checksum cs = new Checksum();
395
              cs.setValue(d.getField("checksum"));
396
              info.setChecksum(cs);
397
              info.setDateSysMetadataModified(dateSysMetadataModified);
398
              Identifier id = new Identifier();
399
              id.setValue(d.getField("identifier"));
400
              info.setIdentifier(id);
401
              info.setObjectFormat(returnedObjectFormat);
402
              info.setSize(d.getField("size"));
403
              //add the ObjectInfo to the ObjectList
404
              ol.addObjectInfo(info);
405
              
406
              //System.out.println(d.toString());
407
          }
408
          
409
          //do an squery on metacat to return systemMetadata docs where 
410
          //sm.dateSysMetadataModified >= startTime
411
          //sm.dateSysMetadataModified <= endTime
412
          //further slice the returned dataset if replicaStatus is false.
413
          //if start != 0, return only the documents with index >= start
414
          //if the number of results is > count, return the first count docs
415
      }
416
      catch(Exception e)
417
      {
418
          e.printStackTrace();
419
          throw new ServiceFailure("1580", "Error retrieving ObjectList: " + e.getMessage());
420
      }
421
      return ol;
422
    }
423
    
424
    /**
425
     * Call listObjects with the default values for replicaStatus (true), start (0),
426
     * and count (1000).
427
     * @param token
428
     * @param startTime
429
     * @param endTime
430
     * @param objectFormat
431
     * @return
432
     * @throws NotAuthorized
433
     * @throws InvalidRequest
434
     * @throws NotImplemented
435
     * @throws ServiceFailure
436
     * @throws InvalidToken
437
     */
438
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
439
        ObjectFormat objectFormat)
440
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
441
    {
442
       return listObjects(token, startTime, endTime, objectFormat, true, 0, 1000);
443
    }
444

    
445
    /**
446
     * Delete a document.  NOT IMPLEMENTED
447
     */
448
    public Identifier delete(AuthToken token, Identifier guid)
449
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
450
            NotImplemented {
451
        throw new NotImplemented("1000", "This method not yet implemented.");
452
    }
453

    
454
    /**
455
     * describe a document.  NOT IMPLEMENTED
456
     */
457
    public DescribeResponse describe(AuthToken token, Identifier guid)
458
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
459
            NotImplemented {
460
        throw new NotImplemented("1000", "This method not yet implemented.");
461
    }
462
    
463
    /**
464
     * get a document with a specified guid.
465
     */
466
    public InputStream get(AuthToken token, Identifier guid)
467
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
468
            NotImplemented {
469
        
470
        // Retrieve the session information from the AuthToken
471
        // If the session is expired, then the user is 'public'
472
        final SessionData sessionData = getSessionData(token);
473
        
474
        // Look up the localId for this global identifier
475
        IdentifierManager im = IdentifierManager.getInstance();
476
        try {
477
            final String localId = im.getLocalId(guid.getValue());
478

    
479
            final InputStreamFromOutputStream<String> objectStream = 
480
                new InputStreamFromOutputStream<String>() {
481
                
482
                @Override
483
                public String produce(final OutputStream dataSink) throws Exception {
484

    
485
                    try {
486
                        handler.readFromMetacat(metacatUrl, null, 
487
                                dataSink, localId, "xml",
488
                                sessionData.getUserName(), 
489
                                sessionData.getGroupNames(), true, params);
490
                    } catch (PropertyNotFoundException e) {
491
                        throw new ServiceFailure("1030", e.getMessage());
492
                    } catch (ClassNotFoundException e) {
493
                        throw new ServiceFailure("1030", e.getMessage());
494
                    } catch (IOException e) {
495
                        throw new ServiceFailure("1030", e.getMessage());
496
                    } catch (SQLException e) {
497
                        throw new ServiceFailure("1030", e.getMessage());
498
                    } catch (McdbException e) {
499
                        throw new ServiceFailure("1030", e.getMessage());
500
                    } catch (ParseLSIDException e) {
501
                        throw new NotFound("1020", e.getMessage());
502
                    } catch (InsufficientKarmaException e) {
503
                        throw new NotAuthorized("1000", "Not authorized for get().");
504
                    }
505

    
506
                    return "Completed";
507
                }
508
            };
509
            return objectStream;
510

    
511
        } catch (McdbDocNotFoundException e) {
512
            throw new NotFound("1020", e.getMessage());
513
        }
514
    }
515

    
516
    /**
517
     * get the checksum for a document.  NOT IMPLEMENTED
518
     */
519
    public Checksum getChecksum(AuthToken token, Identifier guid)
520
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
521
            InvalidRequest, NotImplemented {
522
        throw new NotImplemented("1000", "This method not yet implemented.");
523
    }
524

    
525
    /**
526
     * get the checksum for a document.  NOT IMPLEMENTED
527
     */
528
    public Checksum getChecksum(AuthToken token, Identifier guid, 
529
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
530
            NotAuthorized, NotFound, InvalidRequest, NotImplemented {
531
        throw new NotImplemented("1000", "This method not yet implemented.");
532
    }
533

    
534
    /**
535
     * get log records.  NOT IMPLEMENTED
536
     */
537
    public LogRecordSet getLogRecords(AuthToken token, Date fromDate, Date toDate)
538
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, 
539
            NotImplemented {
540
        throw new NotImplemented("1000", "This method not yet implemented.");
541
    }
542

    
543
    /**
544
     * get the system metadata for a document with a specified guid.
545
     */
546
    public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
547
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
548
            InvalidRequest, NotImplemented {
549
        
550
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
551
        
552
        // Retrieve the session information from the AuthToken
553
        // If the session is expired, then the user is 'public'
554
        final SessionData sessionData = getSessionData(token);
555

    
556
        // TODO: Check access control rules
557
                
558
        try {
559
            IdentifierManager im = IdentifierManager.getInstance();
560
            final String localId = im.getSystemMetadataId(guid.getValue());
561
            
562
            // Read system metadata from metacat's db
563
            final InputStreamFromOutputStream<String> objectStream = 
564
                new InputStreamFromOutputStream<String>() {
565
                
566
                @Override
567
                public String produce(final OutputStream dataSink) throws Exception {
568
                    try {
569
                        handler.readFromMetacat(metacatUrl, null, 
570
                                dataSink, localId, "xml",
571
                                sessionData.getUserName(), 
572
                                sessionData.getGroupNames(), true, params);
573
                    } catch (PropertyNotFoundException e) {
574
                        throw new ServiceFailure("1030", e.getMessage());
575
                    } catch (ClassNotFoundException e) {
576
                        throw new ServiceFailure("1030", e.getMessage());
577
                    } catch (IOException e) {
578
                        throw new ServiceFailure("1030", e.getMessage());
579
                    } catch (SQLException e) {
580
                        throw new ServiceFailure("1030", e.getMessage());
581
                    } catch (McdbException e) {
582
                        throw new ServiceFailure("1030", e.getMessage());
583
                    } catch (ParseLSIDException e) {
584
                        throw new NotFound("1020", e.getMessage());
585
                    } catch (InsufficientKarmaException e) {
586
                        throw new NotAuthorized("1000", "Not authorized for get().");
587
                    }
588

    
589
                    return "Completed";
590
                }
591
            };
592
            
593
            // Deserialize the xml to create a SystemMetadata object
594
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
595
            return sysmeta;
596
            
597
        } catch (McdbDocNotFoundException e) {
598
            //e.printStackTrace();
599
            throw new NotFound("1000", e.getMessage());
600
        }                
601
    }
602

    
603
    /*
604
     * Look up the information on the session using the token provided in
605
     * the AuthToken.  The Session should have all relevant user information.
606
     * If the session has expired or is invalid, the 'public' session will
607
     * be returned, giving the user anonymous access.
608
     */
609
    private static SessionData getSessionData(AuthToken token) {
610
        SessionData sessionData = null;
611
        String sessionId = "PUBLIC";
612
        if (token != null) {
613
            sessionId = token.getToken();
614
        }
615
        
616
        // if the session id is registered in SessionService, get the
617
        // SessionData for it. Otherwise, use the public session.
618
        if (SessionService.isSessionRegistered(sessionId)) {
619
            sessionData = SessionService.getRegisteredSession(sessionId);
620
        } else {
621
            sessionData = SessionService.getPublicSession();
622
        }
623
        
624
        return sessionData;
625
    }
626

    
627
    /** 
628
     * Determine if a given object should be treated as an XML science metadata
629
     * object. 
630
     * 
631
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
632
     * 
633
     * @param sysmeta the SystemMetadata describig the object
634
     * @return true if the object should be treated as science metadata
635
     */
636
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
637
        boolean scimeta = false;
638
        switch (sysmeta.getObjectFormat()) {
639
            case EML_2_1_0: scimeta = true; break;
640
            case EML_2_0_1: scimeta = true; break;
641
            case EML_2_0_0: scimeta = true; break;
642
            case FGDC_STD_001_1_1999: scimeta = true; break;
643
            case FGDC_STD_001_1998: scimeta = true; break;
644
            case NCML_2_2: scimeta = true; break;
645
        }
646
        
647
        return scimeta;
648
    }
649

    
650
    /**
651
     * insert a data doc
652
     * @param object
653
     * @param guid
654
     * @param sessionData
655
     * @throws ServiceFailure
656
     */
657
    private void insertDataObject(InputStream object, Identifier guid, 
658
            SessionData sessionData) throws ServiceFailure {
659
        
660
        String username = sessionData.getUserName();
661
        String[] groups = sessionData.getGroupNames();
662

    
663
        // generate guid/localId pair for object
664
        logMetacat.debug("Generating a guid/localId mapping");
665
        IdentifierManager im = IdentifierManager.getInstance();
666
        String localId = im.generateLocalId(guid.getValue(), 1);
667

    
668
        try {
669
            logMetacat.debug("Case DATA: starting to write to disk.");
670
            if (DocumentImpl.getDataFileLockGrant(localId)) {
671
    
672
                // Save the data file to disk using "localId" as the name
673
                try {
674
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
675
    
676
                    File dataDirectory = new File(datafilepath);
677
                    dataDirectory.mkdirs();
678
    
679
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
680
    
681
                    // TODO: Check that the file size matches SystemMetadata
682
                    //                        long size = newFile.length();
683
                    //                        if (size == 0) {
684
                    //                            throw new IOException("Uploaded file is 0 bytes!");
685
                    //                        }
686
    
687
                    // Register the file in the database (which generates an exception
688
                    // if the localId is not acceptable or other untoward things happen
689
                    try {
690
                        logMetacat.debug("Registering document...");
691
                        System.out.println("inserting data object: localId: " + localId);
692
                        DocumentImpl.registerDocument(localId, "BIN", localId,
693
                                username, groups);
694
                        logMetacat.debug("Registration step completed.");
695
                    } catch (SQLException e) {
696
                        //newFile.delete();
697
                        logMetacat.debug("SQLE: " + e.getMessage());
698
                        e.printStackTrace(System.out);
699
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
700
                    } catch (AccessionNumberException e) {
701
                        //newFile.delete();
702
                        logMetacat.debug("ANE: " + e.getMessage());
703
                        e.printStackTrace(System.out);
704
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
705
                    } catch (Exception e) {
706
                        //newFile.delete();
707
                        logMetacat.debug("Exception: " + e.getMessage());
708
                        e.printStackTrace(System.out);
709
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
710
                    }
711
    
712
                    logMetacat.debug("Logging the creation event.");
713
                    EventLog.getInstance().log(metacatUrl,
714
                            username, localId, "create");
715
    
716
                    // Schedule replication for this data file
717
                    logMetacat.debug("Scheduling replication.");
718
                    ForceReplicationHandler frh = new ForceReplicationHandler(
719
                            localId, "insert", false, null);
720
    
721
                } catch (PropertyNotFoundException e) {
722
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
723
                }
724
    
725
            }
726
        } catch (Exception e) {
727
            // Could not get a lock on the document, so we can not update the file now
728
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
729
        }
730
    }
731

    
732
    /**
733
     * write a file to a stream
734
     * @param dir
735
     * @param fileName
736
     * @param data
737
     * @return
738
     * @throws ServiceFailure
739
     */
740
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
741
        throws ServiceFailure {
742
        
743
        File newFile = new File(dir, fileName);
744
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
745

    
746
        try {
747
            if (newFile.createNewFile()) {
748
                // write data stream to desired file
749
                OutputStream os = new FileOutputStream(newFile);
750
                long length = IOUtils.copyLarge(data, os);
751
                os.flush();
752
                os.close();
753
            } else {
754
                logMetacat.debug("File creation failed, or file already exists.");
755
                throw new ServiceFailure("1190", "File already exists: " + fileName);
756
            }
757
        } catch (FileNotFoundException e) {
758
            logMetacat.debug("FNF: " + e.getMessage());
759
            throw new ServiceFailure("1190", "File not found: " + fileName + " " 
760
                    + e.getMessage());
761
        } catch (IOException e) {
762
            logMetacat.debug("IOE: " + e.getMessage());
763
            throw new ServiceFailure("1190", "File was not written: " + fileName 
764
                    + " " + e.getMessage());
765
        }
766

    
767
        return newFile;
768
    }
769

    
770
    /**
771
     * insert a systemMetadata doc
772
     */
773
    private void insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData) 
774
        throws ServiceFailure {
775
        logMetacat.debug("Starting to insert SystemMetadata...");
776
    
777
        // generate guid/localId pair for sysmeta
778
        Identifier sysMetaGuid = new Identifier();
779
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
780
        sysmeta.setDateSysMetadataModified(new Date());
781

    
782
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
783
        String localId = insertDocument(xml, sysMetaGuid, sessionData);
784
        //insert the system metadata doc id into the identifiers table to 
785
        //link it to the data or metadata document
786
        IdentifierManager.getInstance().createSystemMetadataMapping(sysmeta.getIdentifier().getValue(), sysMetaGuid.getValue());
787
    }
788
    
789
    /**
790
     * update a systemMetadata doc
791
     */
792
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
793
      throws ServiceFailure
794
    {
795
        try
796
        {
797
            String smId = IdentifierManager.getInstance().getSystemMetadataId(sm.getIdentifier().getValue());
798
            sm.setDateSysMetadataModified(new Date());
799
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
800
            Identifier id = new Identifier();
801
            id.setValue(smId);
802
            String localId = updateDocument(xml, id, null, sessionData);
803
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
804
        }
805
        catch(Exception e)
806
        {
807
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getMessage());
808
        }
809
    }
810
    
811
    /**
812
     * insert a document
813
     * NOTE: this method shouldn't be used from the update or create() methods.  
814
     * we shouldn't be putting the science metadata or data objects into memory.
815
     */
816
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
817
        throws ServiceFailure
818
    {
819
        return insertOrUpdateDocument(xml, guid, sessionData, "insert");
820
    }
821
    
822
    /**
823
     * insert a document from a stream
824
     */
825
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
826
      throws IOException, ServiceFailure
827
    {
828
        //HACK: change this eventually.  we should not be converting the stream to a string
829
        String xml = IOUtils.toString(is);
830
        return insertDocument(xml, guid, sessionData);
831
    }
832
    
833
    /**
834
     * update a document
835
     * NOTE: this method shouldn't be used from the update or create() methods.  
836
     * we shouldn't be putting the science metadata or data objects into memory.
837
     */
838
    private String updateDocument(String xml, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
839
        throws ServiceFailure
840
    {
841
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update");
842
    }
843
    
844
    /**
845
     * update a document from a stream
846
     */
847
    private String updateDocument(InputStream is, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
848
      throws IOException, ServiceFailure
849
    {
850
        //HACK: change this eventually.  we should not be converting the stream to a string
851
        String xml = IOUtils.toString(is);
852
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData);
853
        IdentifierManager im = IdentifierManager.getInstance();
854
        if(guid != null)
855
        {
856
          im.createMapping(guid.getValue(), localId);
857
        }
858
        return localId;
859
    }
860
    
861
    /**
862
     * insert a document, return the id of the document that was inserted
863
     */
864
    private String insertOrUpdateDocument(String xml, Identifier guid, SessionData sessionData, String insertOrUpdate) 
865
        throws ServiceFailure {
866
        logMetacat.debug("Starting to insert xml document...");
867
        IdentifierManager im = IdentifierManager.getInstance();
868

    
869
        // generate guid/localId pair for sysmeta
870
        String localId = null;
871
        if(insertOrUpdate.equals("insert"))
872
        {
873
            localId = im.generateLocalId(guid.getValue(), 1);
874
        }
875
        else
876
        {
877
            //localid should already exist in the identifier table, so just find it
878
            try
879
            {
880
                localId = im.getLocalId(guid.getValue());
881
                //increment the revision
882
                String docid = localId.substring(0, localId.lastIndexOf("."));
883
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
884
                int rev = new Integer(revS).intValue();
885
                rev++;
886
                docid = docid + "." + rev;
887
                localId = docid;
888
            }
889
            catch(McdbDocNotFoundException e)
890
            {
891
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
892
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
893
            }
894
        }
895
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
896
                localId);
897

    
898
        String[] action = new String[1];
899
        action[0] = insertOrUpdate;
900
        params.put("action", action);
901
        String[] docid = new String[1];
902
        docid[0] = localId;
903
        params.put("docid", docid);
904
        String[] doctext = new String[1];
905
        doctext[0] = xml;
906
        logMetacat.debug(doctext[0]);
907
        params.put("doctext", doctext);
908
        
909
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
910
        // onto output stream, or alternatively, capture that and parse it to 
911
        // generate the right exceptions
912
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
913
        //PrintWriter pw = new PrintWriter(output);
914
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
915
                            null, params, sessionData.getUserName(), sessionData.getGroupNames());
916
        //String outputS = new String(output.toByteArray());
917
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
918
//        if (!(outputS.indexOf("<success>") > 0 && outputS.indexOf(localId) > 0)) {
919
//            throw new ServiceFailure(1190, outputS);
920
//        }
921
        logMetacat.debug("Finsished inserting xml document with id " + localId);
922
        return localId;
923
    }
924
    
925
    /**
926
     * serialize a system metadata doc
927
     * @param sysmeta
928
     * @return
929
     * @throws ServiceFailure
930
     */
931
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta) 
932
        throws ServiceFailure {
933
        IBindingFactory bfact;
934
        ByteArrayOutputStream sysmetaOut = null;
935
        try {
936
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
937
            IMarshallingContext mctx = bfact.createMarshallingContext();
938
            sysmetaOut = new ByteArrayOutputStream();
939
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
940
        } catch (JiBXException e) {
941
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
942
        }
943
        
944
        return sysmetaOut;
945
    }
946
    
947
    /**
948
     * deserialize a system metadata doc
949
     * @param xml
950
     * @return
951
     * @throws ServiceFailure
952
     */
953
    public static SystemMetadata deserializeSystemMetadata(InputStream xml) 
954
        throws ServiceFailure {
955
        try {
956
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
957
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
958
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
959
            return sysmeta;
960
        } catch (JiBXException e) {
961
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
962
        }    
963
    }
964
}
    (1-1/1)