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

    
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.AuthToken;
58
import org.dataone.service.types.Checksum;
59
import org.dataone.service.types.DescribeResponse;
60
import org.dataone.service.types.Identifier;
61
import org.dataone.service.types.LogRecordSet;
62
import org.dataone.service.types.SystemMetadata;
63
import org.dataone.service.types.ObjectList;
64
import org.dataone.service.types.ObjectFormat;
65
import org.jibx.runtime.BindingDirectory;
66
import org.jibx.runtime.IBindingFactory;
67
import org.jibx.runtime.IMarshallingContext;
68
import org.jibx.runtime.IUnmarshallingContext;
69
import org.jibx.runtime.JiBXException;
70

    
71
import com.gc.iotools.stream.is.InputStreamFromOutputStream;
72

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

    
91
/**
92
 * 
93
 * Implements DataONE MemberNode CRUD API for Metacat. 
94
 * 
95
 * @author Matthew Jones
96
 */
97
public class CrudService implements MemberNodeCrud {
98

    
99
    /*private ServletContext servletContext;
100
    private HttpServletRequest request;
101
    private HttpServletResponse response;*/
102
    
103
    private static CrudService crudService = null;
104

    
105
    private MetacatHandler handler;
106
    private Hashtable<String, String[]> params;
107
    Logger logMetacat = null;
108
    
109
    private String metacatUrl;
110

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

    
153
        handler = new MetacatHandler(new Timer());
154

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

    
191
        logMetacat.debug("Starting CrudService.create()...");
192
        
193
        // authenticate & get user info
194
        SessionData sessionData = getSessionData(token);
195
        String username = sessionData.getUserName();
196
        String[] groups = sessionData.getGroupNames();
197

    
198
        // verify that guid == SystemMetadata.getIdentifier()
199
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
200
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
201
            throw new InvalidSystemMetadata("1180", 
202
                "GUID in method call does not match GUID in system metadata.");
203
        }
204

    
205
        logMetacat.debug("Checking if identifier exists...");
206
        // Check that the identifier does not already exist
207
        IdentifierManager im = IdentifierManager.getInstance();
208
        if (im.identifierExists(guid.getValue())) {
209
            throw new IdentifierNotUnique("1120", 
210
                "GUID is already in use by an existing object.");
211
        }
212

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

    
227
        } else {
228
            // DEFAULT CASE: DATA (needs to be checked and completed)
229
            insertDataObject(object, guid, sessionData);
230
            
231
        }
232

    
233
        // For Metadata and Data, insert the system metadata into the object store too
234
        insertSystemMetadata(sysmeta, sessionData);
235

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

    
410
    /**
411
     * Delete a document.  NOT IMPLEMENTED
412
     */
413
    public Identifier delete(AuthToken token, Identifier guid)
414
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
415
            NotImplemented {
416
        throw new NotImplemented("1000", "This method not yet implemented.");
417
    }
418

    
419
    /**
420
     * describe a document.  NOT IMPLEMENTED
421
     */
422
    public DescribeResponse describe(AuthToken token, Identifier guid)
423
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
424
            NotImplemented {
425
        throw new NotImplemented("1000", "This method not yet implemented.");
426
    }
427
    
428
    /**
429
     * get a document with a specified guid.
430
     */
431
    public InputStream get(AuthToken token, Identifier guid)
432
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
433
            NotImplemented {
434
        
435
        // Retrieve the session information from the AuthToken
436
        // If the session is expired, then the user is 'public'
437
        final SessionData sessionData = getSessionData(token);
438
        
439
        // Look up the localId for this global identifier
440
        IdentifierManager im = IdentifierManager.getInstance();
441
        try {
442
            final String localId = im.getLocalId(guid.getValue());
443

    
444
            final InputStreamFromOutputStream<String> objectStream = 
445
                new InputStreamFromOutputStream<String>() {
446
                
447
                @Override
448
                public String produce(final OutputStream dataSink) throws Exception {
449

    
450
                    try {
451
                        handler.readFromMetacat(metacatUrl, null, 
452
                                dataSink, localId, "xml",
453
                                sessionData.getUserName(), 
454
                                sessionData.getGroupNames(), true, params);
455
                    } catch (PropertyNotFoundException e) {
456
                        throw new ServiceFailure("1030", e.getMessage());
457
                    } catch (ClassNotFoundException e) {
458
                        throw new ServiceFailure("1030", e.getMessage());
459
                    } catch (IOException e) {
460
                        throw new ServiceFailure("1030", e.getMessage());
461
                    } catch (SQLException e) {
462
                        throw new ServiceFailure("1030", e.getMessage());
463
                    } catch (McdbException e) {
464
                        throw new ServiceFailure("1030", e.getMessage());
465
                    } catch (ParseLSIDException e) {
466
                        throw new NotFound("1020", e.getMessage());
467
                    } catch (InsufficientKarmaException e) {
468
                        throw new NotAuthorized("1000", "Not authorized for get().");
469
                    }
470

    
471
                    return "Completed";
472
                }
473
            };
474
            return objectStream;
475

    
476
        } catch (McdbDocNotFoundException e) {
477
            throw new NotFound("1020", e.getMessage());
478
        }
479
    }
480

    
481
    /**
482
     * get the checksum for a document.  NOT IMPLEMENTED
483
     */
484
    public Checksum getChecksum(AuthToken token, Identifier guid)
485
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
486
            InvalidRequest, NotImplemented {
487
        throw new NotImplemented("1000", "This method not yet implemented.");
488
    }
489

    
490
    /**
491
     * get the checksum for a document.  NOT IMPLEMENTED
492
     */
493
    public Checksum getChecksum(AuthToken token, Identifier guid, 
494
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
495
            NotAuthorized, NotFound, InvalidRequest, NotImplemented {
496
        throw new NotImplemented("1000", "This method not yet implemented.");
497
    }
498

    
499
    /**
500
     * get log records.  NOT IMPLEMENTED
501
     */
502
    public LogRecordSet getLogRecords(AuthToken token, Date fromDate, Date toDate)
503
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, 
504
            NotImplemented {
505
        throw new NotImplemented("1000", "This method not yet implemented.");
506
    }
507

    
508
    /**
509
     * get the system metadata for a document with a specified guid.
510
     */
511
    public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
512
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
513
            InvalidRequest, NotImplemented {
514
        
515
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
516
        
517
        // Retrieve the session information from the AuthToken
518
        // If the session is expired, then the user is 'public'
519
        final SessionData sessionData = getSessionData(token);
520

    
521
        // TODO: Check access control rules
522
                
523
        try {
524
            IdentifierManager im = IdentifierManager.getInstance();
525
            final String localId = im.getSystemMetadataId(guid.getValue());
526
            
527
            // Read system metadata from metacat's db
528
            final InputStreamFromOutputStream<String> objectStream = 
529
                new InputStreamFromOutputStream<String>() {
530
                
531
                @Override
532
                public String produce(final OutputStream dataSink) throws Exception {
533
                    try {
534
                        handler.readFromMetacat(metacatUrl, null, 
535
                                dataSink, localId, "xml",
536
                                sessionData.getUserName(), 
537
                                sessionData.getGroupNames(), true, params);
538
                    } catch (PropertyNotFoundException e) {
539
                        throw new ServiceFailure("1030", e.getMessage());
540
                    } catch (ClassNotFoundException e) {
541
                        throw new ServiceFailure("1030", e.getMessage());
542
                    } catch (IOException e) {
543
                        throw new ServiceFailure("1030", e.getMessage());
544
                    } catch (SQLException e) {
545
                        throw new ServiceFailure("1030", e.getMessage());
546
                    } catch (McdbException e) {
547
                        throw new ServiceFailure("1030", e.getMessage());
548
                    } catch (ParseLSIDException e) {
549
                        throw new NotFound("1020", e.getMessage());
550
                    } catch (InsufficientKarmaException e) {
551
                        throw new NotAuthorized("1000", "Not authorized for get().");
552
                    }
553

    
554
                    return "Completed";
555
                }
556
            };
557
            
558
            // Deserialize the xml to create a SystemMetadata object
559
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
560
            return sysmeta;
561
            
562
        } catch (McdbDocNotFoundException e) {
563
            //e.printStackTrace();
564
            throw new NotFound("1000", e.getMessage());
565
        }                
566
    }
567

    
568
    /*
569
     * Look up the information on the session using the token provided in
570
     * the AuthToken.  The Session should have all relevant user information.
571
     * If the session has expired or is invalid, the 'public' session will
572
     * be returned, giving the user anonymous access.
573
     */
574
    private static SessionData getSessionData(AuthToken token) {
575
        SessionData sessionData = null;
576
        String sessionId = "PUBLIC";
577
        if (token != null) {
578
            sessionId = token.getToken();
579
        }
580
        
581
        // if the session id is registered in SessionService, get the
582
        // SessionData for it. Otherwise, use the public session.
583
        if (SessionService.isSessionRegistered(sessionId)) {
584
            sessionData = SessionService.getRegisteredSession(sessionId);
585
        } else {
586
            sessionData = SessionService.getPublicSession();
587
        }
588
        
589
        return sessionData;
590
    }
591

    
592
    /** 
593
     * Determine if a given object should be treated as an XML science metadata
594
     * object. 
595
     * 
596
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
597
     * 
598
     * @param sysmeta the SystemMetadata describig the object
599
     * @return true if the object should be treated as science metadata
600
     */
601
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
602
        boolean scimeta = false;
603
        switch (sysmeta.getObjectFormat()) {
604
            case EML_2_1_0: scimeta = true; break;
605
            case EML_2_0_1: scimeta = true; break;
606
            case EML_2_0_0: scimeta = true; break;
607
            case FGDC_STD_001_1_1999: scimeta = true; break;
608
            case FGDC_STD_001_1998: scimeta = true; break;
609
            case NCML_2_2: scimeta = true; break;
610
        }
611
        
612
        return scimeta;
613
    }
614

    
615
    /**
616
     * insert a data doc
617
     * @param object
618
     * @param guid
619
     * @param sessionData
620
     * @throws ServiceFailure
621
     */
622
    private void insertDataObject(InputStream object, Identifier guid, 
623
            SessionData sessionData) throws ServiceFailure {
624
        
625
        String username = sessionData.getUserName();
626
        String[] groups = sessionData.getGroupNames();
627

    
628
        // generate guid/localId pair for object
629
        logMetacat.debug("Generating a guid/localId mapping");
630
        IdentifierManager im = IdentifierManager.getInstance();
631
        String localId = im.generateLocalId(guid.getValue(), 1);
632

    
633
        try {
634
            logMetacat.debug("Case DATA: starting to write to disk.");
635
            if (DocumentImpl.getDataFileLockGrant(localId)) {
636
    
637
                // Save the data file to disk using "localId" as the name
638
                try {
639
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
640
    
641
                    File dataDirectory = new File(datafilepath);
642
                    dataDirectory.mkdirs();
643
    
644
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
645
    
646
                    // TODO: Check that the file size matches SystemMetadata
647
                    //                        long size = newFile.length();
648
                    //                        if (size == 0) {
649
                    //                            throw new IOException("Uploaded file is 0 bytes!");
650
                    //                        }
651
    
652
                    // Register the file in the database (which generates an exception
653
                    // if the localId is not acceptable or other untoward things happen
654
                    try {
655
                        logMetacat.debug("Registering document...");
656
                        System.out.println("inserting data object: localId: " + localId);
657
                        DocumentImpl.registerDocument(localId, "BIN", localId,
658
                                username, groups);
659
                        logMetacat.debug("Registration step completed.");
660
                    } catch (SQLException e) {
661
                        //newFile.delete();
662
                        logMetacat.debug("SQLE: " + e.getMessage());
663
                        e.printStackTrace(System.out);
664
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
665
                    } catch (AccessionNumberException e) {
666
                        //newFile.delete();
667
                        logMetacat.debug("ANE: " + e.getMessage());
668
                        e.printStackTrace(System.out);
669
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
670
                    } catch (Exception e) {
671
                        //newFile.delete();
672
                        logMetacat.debug("Exception: " + e.getMessage());
673
                        e.printStackTrace(System.out);
674
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
675
                    }
676
    
677
                    logMetacat.debug("Logging the creation event.");
678
                    EventLog.getInstance().log(metacatUrl,
679
                            username, localId, "create");
680
    
681
                    // Schedule replication for this data file
682
                    logMetacat.debug("Scheduling replication.");
683
                    ForceReplicationHandler frh = new ForceReplicationHandler(
684
                            localId, "insert", false, null);
685
    
686
                } catch (PropertyNotFoundException e) {
687
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
688
                }
689
    
690
            }
691
        } catch (Exception e) {
692
            // Could not get a lock on the document, so we can not update the file now
693
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
694
        }
695
    }
696

    
697
    /**
698
     * write a file to a stream
699
     * @param dir
700
     * @param fileName
701
     * @param data
702
     * @return
703
     * @throws ServiceFailure
704
     */
705
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
706
        throws ServiceFailure {
707
        
708
        File newFile = new File(dir, fileName);
709
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
710

    
711
        try {
712
            if (newFile.createNewFile()) {
713
                // write data stream to desired file
714
                OutputStream os = new FileOutputStream(newFile);
715
                long length = IOUtils.copyLarge(data, os);
716
                os.flush();
717
                os.close();
718
            } else {
719
                logMetacat.debug("File creation failed, or file already exists.");
720
                throw new ServiceFailure("1190", "File already exists: " + fileName);
721
            }
722
        } catch (FileNotFoundException e) {
723
            logMetacat.debug("FNF: " + e.getMessage());
724
            throw new ServiceFailure("1190", "File not found: " + fileName + " " 
725
                    + e.getMessage());
726
        } catch (IOException e) {
727
            logMetacat.debug("IOE: " + e.getMessage());
728
            throw new ServiceFailure("1190", "File was not written: " + fileName 
729
                    + " " + e.getMessage());
730
        }
731

    
732
        return newFile;
733
    }
734

    
735
    /**
736
     * insert a systemMetadata doc
737
     */
738
    private void insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData) 
739
        throws ServiceFailure {
740
        logMetacat.debug("Starting to insert SystemMetadata...");
741
    
742
        // generate guid/localId pair for sysmeta
743
        Identifier sysMetaGuid = new Identifier();
744
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
745
        sysmeta.setDateSysMetadataModified(new Date());
746

    
747
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
748
        String localId = insertDocument(xml, sysMetaGuid, sessionData);
749
        //insert the system metadata doc id into the identifiers table to 
750
        //link it to the data or metadata document
751
        IdentifierManager.getInstance().createSystemMetadataMapping(sysmeta.getIdentifier().getValue(), sysMetaGuid.getValue());
752
    }
753
    
754
    /**
755
     * update a systemMetadata doc
756
     */
757
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
758
      throws ServiceFailure
759
    {
760
        try
761
        {
762
            String smId = IdentifierManager.getInstance().getSystemMetadataId(sm.getIdentifier().getValue());
763
            sm.setDateSysMetadataModified(new Date());
764
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
765
            Identifier id = new Identifier();
766
            id.setValue(smId);
767
            String localId = updateDocument(xml, id, null, sessionData);
768
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
769
        }
770
        catch(Exception e)
771
        {
772
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getMessage());
773
        }
774
    }
775
    
776
    /**
777
     * insert a document
778
     * NOTE: this method shouldn't be used from the update or create() methods.  
779
     * we shouldn't be putting the science metadata or data objects into memory.
780
     */
781
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
782
        throws ServiceFailure
783
    {
784
        return insertOrUpdateDocument(xml, guid, sessionData, "insert");
785
    }
786
    
787
    /**
788
     * insert a document from a stream
789
     */
790
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
791
      throws IOException, ServiceFailure
792
    {
793
        //HACK: change this eventually.  we should not be converting the stream to a string
794
        String xml = IOUtils.toString(is);
795
        return insertDocument(xml, guid, sessionData);
796
    }
797
    
798
    /**
799
     * update a document
800
     * NOTE: this method shouldn't be used from the update or create() methods.  
801
     * we shouldn't be putting the science metadata or data objects into memory.
802
     */
803
    private String updateDocument(String xml, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
804
        throws ServiceFailure
805
    {
806
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update");
807
    }
808
    
809
    /**
810
     * update a document from a stream
811
     */
812
    private String updateDocument(InputStream is, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
813
      throws IOException, ServiceFailure
814
    {
815
        //HACK: change this eventually.  we should not be converting the stream to a string
816
        String xml = IOUtils.toString(is);
817
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData);
818
        IdentifierManager im = IdentifierManager.getInstance();
819
        if(guid != null)
820
        {
821
          im.createMapping(guid.getValue(), localId);
822
        }
823
        return localId;
824
    }
825
    
826
    /**
827
     * insert a document, return the id of the document that was inserted
828
     */
829
    private String insertOrUpdateDocument(String xml, Identifier guid, SessionData sessionData, String insertOrUpdate) 
830
        throws ServiceFailure {
831
        logMetacat.debug("Starting to insert xml document...");
832
        IdentifierManager im = IdentifierManager.getInstance();
833

    
834
        // generate guid/localId pair for sysmeta
835
        String localId = null;
836
        if(insertOrUpdate.equals("insert"))
837
        {
838
            localId = im.generateLocalId(guid.getValue(), 1);
839
        }
840
        else
841
        {
842
            //localid should already exist in the identifier table, so just find it
843
            try
844
            {
845
                localId = im.getLocalId(guid.getValue());
846
                //increment the revision
847
                String docid = localId.substring(0, localId.lastIndexOf("."));
848
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
849
                int rev = new Integer(revS).intValue();
850
                rev++;
851
                docid = docid + "." + rev;
852
                localId = docid;
853
            }
854
            catch(McdbDocNotFoundException e)
855
            {
856
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
857
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
858
            }
859
        }
860
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
861
                localId);
862

    
863
        String[] action = new String[1];
864
        action[0] = insertOrUpdate;
865
        params.put("action", action);
866
        String[] docid = new String[1];
867
        docid[0] = localId;
868
        params.put("docid", docid);
869
        String[] doctext = new String[1];
870
        doctext[0] = xml;
871
        logMetacat.debug(doctext[0]);
872
        params.put("doctext", doctext);
873
        
874
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
875
        // onto output stream, or alternatively, capture that and parse it to 
876
        // generate the right exceptions
877
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
878
        //PrintWriter pw = new PrintWriter(output);
879
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
880
                            null, params, sessionData.getUserName(), sessionData.getGroupNames());
881
        //String outputS = new String(output.toByteArray());
882
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
883
//        if (!(outputS.indexOf("<success>") > 0 && outputS.indexOf(localId) > 0)) {
884
//            throw new ServiceFailure(1190, outputS);
885
//        }
886
        logMetacat.debug("Finsished inserting xml document with id " + localId);
887
        return localId;
888
    }
889
    
890
    /**
891
     * serialize a system metadata doc
892
     * @param sysmeta
893
     * @return
894
     * @throws ServiceFailure
895
     */
896
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta) 
897
        throws ServiceFailure {
898
        IBindingFactory bfact;
899
        ByteArrayOutputStream sysmetaOut = null;
900
        try {
901
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
902
            IMarshallingContext mctx = bfact.createMarshallingContext();
903
            sysmetaOut = new ByteArrayOutputStream();
904
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
905
        } catch (JiBXException e) {
906
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
907
        }
908
        
909
        return sysmetaOut;
910
    }
911
    
912
    /**
913
     * deserialize a system metadata doc
914
     * @param xml
915
     * @return
916
     * @throws ServiceFailure
917
     */
918
    public static SystemMetadata deserializeSystemMetadata(InputStream xml) 
919
        throws ServiceFailure {
920
        try {
921
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
922
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
923
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
924
            return sysmeta;
925
        } catch (JiBXException e) {
926
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
927
        }    
928
    }
929
}
    (1-1/1)