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.jibx.runtime.BindingDirectory;
64
import org.jibx.runtime.IBindingFactory;
65
import org.jibx.runtime.IMarshallingContext;
66
import org.jibx.runtime.IUnmarshallingContext;
67
import org.jibx.runtime.JiBXException;
68

    
69
import com.gc.iotools.stream.is.InputStreamFromOutputStream;
70

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

    
87
/**
88
 * 
89
 * Implements DataONE MemberNode CRUD API for Metacat. 
90
 * 
91
 * @author Matthew Jones
92
 */
93
public class CrudService implements MemberNodeCrud {
94

    
95
    /*private ServletContext servletContext;
96
    private HttpServletRequest request;
97
    private HttpServletResponse response;*/
98
    
99
    private static CrudService crudService = null;
100

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

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

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

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

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

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

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

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

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

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

    
232
        logMetacat.debug("Returning from CrudService.create()");
233
        return guid;
234
    }
235

    
236
    public Identifier delete(AuthToken token, Identifier guid)
237
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
238
            NotImplemented {
239
        throw new NotImplemented(1000, "This method not yet implemented.");
240
    }
241

    
242
    public DescribeResponse describe(AuthToken token, Identifier guid)
243
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
244
            NotImplemented {
245
        throw new NotImplemented(1000, "This method not yet implemented.");
246
    }
247
    
248
    public InputStream get(AuthToken token, Identifier guid)
249
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
250
            NotImplemented {
251
        
252
        // Retrieve the session information from the AuthToken
253
        // If the session is expired, then the user is 'public'
254
        final SessionData sessionData = getSessionData(token);
255
        
256
        // Look up the localId for this global identifier
257
        IdentifierManager im = IdentifierManager.getInstance();
258
        try {
259
            final String localId = im.getLocalId(guid.getValue());
260

    
261
            final InputStreamFromOutputStream<String> objectStream = 
262
                new InputStreamFromOutputStream<String>() {
263
                
264
                @Override
265
                public String produce(final OutputStream dataSink) throws Exception {
266

    
267
                    try {
268
                        handler.readFromMetacat(metacatUrl, null, 
269
                                dataSink, localId, "xml",
270
                                sessionData.getUserName(), 
271
                                sessionData.getGroupNames(), true, params);
272
                    } catch (PropertyNotFoundException e) {
273
                        throw new ServiceFailure(1030, e.getMessage());
274
                    } catch (ClassNotFoundException e) {
275
                        throw new ServiceFailure(1030, e.getMessage());
276
                    } catch (IOException e) {
277
                        throw new ServiceFailure(1030, e.getMessage());
278
                    } catch (SQLException e) {
279
                        throw new ServiceFailure(1030, e.getMessage());
280
                    } catch (McdbException e) {
281
                        throw new ServiceFailure(1030, e.getMessage());
282
                    } catch (ParseLSIDException e) {
283
                        throw new NotFound(1020, e.getMessage());
284
                    } catch (InsufficientKarmaException e) {
285
                        throw new NotAuthorized(1000, "Not authorized for get().");
286
                    }
287

    
288
                    return "Completed";
289
                }
290
            };
291
            return objectStream;
292

    
293
        } catch (McdbDocNotFoundException e) {
294
            throw new NotFound(1020, e.getMessage());
295
        }
296
    }
297

    
298
    public Checksum getChecksum(AuthToken token, Identifier guid)
299
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
300
            InvalidRequest, NotImplemented {
301
        throw new NotImplemented(1000, "This method not yet implemented.");
302
    }
303

    
304
    public Checksum getChecksum(AuthToken token, Identifier guid, 
305
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
306
            NotAuthorized, NotFound, InvalidRequest, NotImplemented {
307
        throw new NotImplemented(1000, "This method not yet implemented.");
308
    }
309

    
310
    public LogRecordSet getLogRecords(AuthToken token, Date fromDate, Date toDate)
311
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, 
312
            NotImplemented {
313
        throw new NotImplemented(1000, "This method not yet implemented.");
314
    }
315

    
316
    public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
317
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
318
            InvalidRequest, NotImplemented {
319
        
320
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
321
        
322
        // Retrieve the session information from the AuthToken
323
        // If the session is expired, then the user is 'public'
324
        final SessionData sessionData = getSessionData(token);
325

    
326
        // TODO: Check access control rules
327
                
328
        try {
329
            IdentifierManager im = IdentifierManager.getInstance();
330
            final String localId = im.getSystemMetadataId(guid.getValue());
331
            
332
            // Read system metadata from metacat's db
333
            final InputStreamFromOutputStream<String> objectStream = 
334
                new InputStreamFromOutputStream<String>() {
335
                
336
                @Override
337
                public String produce(final OutputStream dataSink) throws Exception {
338
                    try {
339
                        handler.readFromMetacat(metacatUrl, null, 
340
                                dataSink, localId, "xml",
341
                                sessionData.getUserName(), 
342
                                sessionData.getGroupNames(), true, params);
343
                    } catch (PropertyNotFoundException e) {
344
                        throw new ServiceFailure(1030, e.getMessage());
345
                    } catch (ClassNotFoundException e) {
346
                        throw new ServiceFailure(1030, e.getMessage());
347
                    } catch (IOException e) {
348
                        throw new ServiceFailure(1030, e.getMessage());
349
                    } catch (SQLException e) {
350
                        throw new ServiceFailure(1030, e.getMessage());
351
                    } catch (McdbException e) {
352
                        throw new ServiceFailure(1030, e.getMessage());
353
                    } catch (ParseLSIDException e) {
354
                        throw new NotFound(1020, e.getMessage());
355
                    } catch (InsufficientKarmaException e) {
356
                        throw new NotAuthorized(1000, "Not authorized for get().");
357
                    }
358

    
359
                    return "Completed";
360
                }
361
            };
362
            
363
            // Deserialize the xml to create a SystemMetadata object
364
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
365
            return sysmeta;
366
            
367
        } catch (McdbDocNotFoundException e) {
368
            //e.printStackTrace();
369
            throw new NotFound(1000, e.getMessage());
370
        }                
371
    }
372

    
373
    /**
374
     * update an existing object with a new object.  Change the system metadata
375
     * to reflect the changes and update it as well.
376
     */
377
    public Identifier update(AuthToken token, Identifier guid, 
378
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta) 
379
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
380
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
381
            NotImplemented {
382
        try
383
        {
384
            SessionData sessionData = getSessionData(token);
385
            
386
            //find the old systemmetadata (sm.old) document id (the one linked to obsoletedGuid)
387
            SystemMetadata sm = getSystemMetadata(token, obsoletedGuid);
388
            //change sm.old's obsoletedBy field 
389
            List l = sm.getObsoletedByList();
390
            l.add(guid);
391
            sm.setObsoletedByList(l);
392
            //update sm.old
393
            updateSystemMetadata(sm, sessionData);
394
            
395
            //change the obsoletes field of the new systemMetadata (sm.new) to point to the id of the old one
396
            sysmeta.addObsolete(obsoletedGuid);
397
            //insert sm.new
398
            insertSystemMetadata(sysmeta, sessionData);
399
            
400
            //update the doc
401
            updateDocument(object, obsoletedGuid, guid, sessionData);
402
            return guid;
403
        }
404
        catch(Exception e)
405
        {
406
            throw new ServiceFailure(1030, "Error updating document in CrudService: " + e.getMessage());
407
        }
408
    }
409

    
410
    /*
411
     * Look up the information on the session using the token provided in
412
     * the AuthToken.  The Session should have all relevant user information.
413
     * If the session has expired or is invalid, the 'public' session will
414
     * be returned, giving the user anonymous access.
415
     */
416
    private static SessionData getSessionData(AuthToken token) {
417
        SessionData sessionData = null;
418
        String sessionId = "PUBLIC";
419
        if (token != null) {
420
            sessionId = token.getToken();
421
        }
422
        
423
        // if the session id is registered in SessionService, get the
424
        // SessionData for it. Otherwise, use the public session.
425
        if (SessionService.isSessionRegistered(sessionId)) {
426
            sessionData = SessionService.getRegisteredSession(sessionId);
427
        } else {
428
            sessionData = SessionService.getPublicSession();
429
        }
430
        
431
        return sessionData;
432
    }
433

    
434
    /** 
435
     * Determine if a given object should be treated as an XML science metadata
436
     * object. 
437
     * 
438
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
439
     * 
440
     * @param sysmeta the SystemMetadata describig the object
441
     * @return true if the object should be treated as science metadata
442
     */
443
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
444
        boolean scimeta = false;
445
        switch (sysmeta.getObjectFormat()) {
446
            case EML_2_1_0: scimeta = true; break;
447
            case EML_2_0_1: scimeta = true; break;
448
            case EML_2_0_0: scimeta = true; break;
449
            case FGDC_STD_001_1_1999: scimeta = true; break;
450
            case FGDC_STD_001_1998: scimeta = true; break;
451
            case NCML_2_2: scimeta = true; break;
452
        }
453
        
454
        return scimeta;
455
    }
456

    
457
    private void insertDataObject(InputStream object, Identifier guid, 
458
            SessionData sessionData) throws ServiceFailure {
459
        
460
        String username = sessionData.getUserName();
461
        String[] groups = sessionData.getGroupNames();
462

    
463
        // generate guid/localId pair for object
464
        logMetacat.debug("Generating a guid/localId mapping");
465
        IdentifierManager im = IdentifierManager.getInstance();
466
        String localId = im.generateLocalId(guid.getValue(), 1);
467

    
468
        try {
469
            logMetacat.debug("Case DATA: starting to write to disk.");
470
            if (DocumentImpl.getDataFileLockGrant(localId)) {
471
    
472
                // Save the data file to disk using "localId" as the name
473
                try {
474
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
475
    
476
                    File dataDirectory = new File(datafilepath);
477
                    dataDirectory.mkdirs();
478
    
479
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
480
    
481
                    // TODO: Check that the file size matches SystemMetadata
482
                    //                        long size = newFile.length();
483
                    //                        if (size == 0) {
484
                    //                            throw new IOException("Uploaded file is 0 bytes!");
485
                    //                        }
486
    
487
                    // Register the file in the database (which generates an exception
488
                    // if the localId is not acceptable or other untoward things happen
489
                    try {
490
                        logMetacat.debug("Registering document...");
491
                        DocumentImpl.registerDocument(localId, "BIN", localId,
492
                                username, groups);
493
                        logMetacat.debug("Registration step completed.");
494
                    } catch (SQLException e) {
495
                        //newFile.delete();
496
                        logMetacat.debug("SQLE: " + e.getMessage());
497
                        e.printStackTrace(System.out);
498
                        throw new ServiceFailure(1190, "Registration failed: " + e.getMessage());
499
                    } catch (AccessionNumberException e) {
500
                        //newFile.delete();
501
                        logMetacat.debug("ANE: " + e.getMessage());
502
                        e.printStackTrace(System.out);
503
                        throw new ServiceFailure(1190, "Registration failed: " + e.getMessage());
504
                    } catch (Exception e) {
505
                        //newFile.delete();
506
                        logMetacat.debug("Exception: " + e.getMessage());
507
                        e.printStackTrace(System.out);
508
                        throw new ServiceFailure(1190, "Registration failed: " + e.getMessage());
509
                    }
510
    
511
                    logMetacat.debug("Logging the creation event.");
512
                    EventLog.getInstance().log(metacatUrl,
513
                            username, localId, "create");
514
    
515
                    // Schedule replication for this data file
516
                    logMetacat.debug("Scheduling replication.");
517
                    ForceReplicationHandler frh = new ForceReplicationHandler(
518
                            localId, "insert", false, null);
519
    
520
                } catch (PropertyNotFoundException e) {
521
                    throw new ServiceFailure(1190, "Could not lock file for writing:" + e.getMessage());
522
                }
523
    
524
            }
525
        } catch (Exception e) {
526
            // Could not get a lock on the document, so we can not update the file now
527
            throw new ServiceFailure(1190, "Failed to lock file: " + e.getMessage());
528
        }
529
    }
530

    
531
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
532
        throws ServiceFailure {
533
        
534
        File newFile = new File(dir, fileName);
535
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
536

    
537
        try {
538
            if (newFile.createNewFile()) {
539
                // write data stream to desired file
540
                OutputStream os = new FileOutputStream(newFile);
541
                long length = IOUtils.copyLarge(data, os);
542
                os.flush();
543
                os.close();
544
            } else {
545
                logMetacat.debug("File creation failed, or file already exists.");
546
                throw new ServiceFailure(1190, "File already exists: " + fileName);
547
            }
548
        } catch (FileNotFoundException e) {
549
            logMetacat.debug("FNF: " + e.getMessage());
550
            throw new ServiceFailure(1190, "File not found: " + fileName + " " 
551
                    + e.getMessage());
552
        } catch (IOException e) {
553
            logMetacat.debug("IOE: " + e.getMessage());
554
            throw new ServiceFailure(1190, "File was not written: " + fileName 
555
                    + " " + e.getMessage());
556
        }
557

    
558
        return newFile;
559
    }
560

    
561
    /**
562
     * insert a systemMetadata doc
563
     */
564
    private void insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData) 
565
        throws ServiceFailure {
566
        logMetacat.debug("Starting to insert SystemMetadata...");
567
    
568
        // generate guid/localId pair for sysmeta
569
        Identifier sysMetaGuid = new Identifier();
570
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
571
        sysmeta.setDateSysMetadataModified(new Date());
572

    
573
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
574
        String localId = insertDocument(xml, sysMetaGuid, sessionData);
575
        //insert the system metadata doc id into the identifiers table to 
576
        //link it to the data or metadata document
577
        IdentifierManager.getInstance().createSystemMetadataMapping(sysmeta.getIdentifier().getValue(), sysMetaGuid.getValue());
578
    }
579
    
580
    /**
581
     * update a systemMetadata doc
582
     */
583
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
584
      throws ServiceFailure
585
    {
586
        try
587
        {
588
            String smId = IdentifierManager.getInstance().getSystemMetadataId(sm.getIdentifier().getValue());
589
            sm.setDateSysMetadataModified(new Date());
590
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
591
            Identifier id = new Identifier();
592
            id.setValue(smId);
593
            String localId = updateDocument(xml, id, null, sessionData);
594
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
595
        }
596
        catch(Exception e)
597
        {
598
            throw new ServiceFailure(1030, "Error updating system metadata: " + e.getMessage());
599
        }
600
    }
601
    
602
    /**
603
     * insert a document
604
     * NOTE: this method shouldn't be used from the update or create() methods.  
605
     * we shouldn't be putting the science metadata or data objects into memory.
606
     */
607
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
608
        throws ServiceFailure
609
    {
610
        return insertOrUpdateDocument(xml, guid, sessionData, "insert");
611
    }
612
    
613
    /**
614
     * insert a document from a stream
615
     */
616
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
617
      throws IOException, ServiceFailure
618
    {
619
        //HACK: change this eventually.  we should not be converting the stream to a string
620
        String xml = IOUtils.toString(is);
621
        return insertDocument(xml, guid, sessionData);
622
    }
623
    
624
    /**
625
     * update a document
626
     * NOTE: this method shouldn't be used from the update or create() methods.  
627
     * we shouldn't be putting the science metadata or data objects into memory.
628
     */
629
    private String updateDocument(String xml, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
630
        throws ServiceFailure
631
    {
632
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update");
633
    }
634
    
635
    /**
636
     * update a document from a stream
637
     */
638
    private String updateDocument(InputStream is, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
639
      throws IOException, ServiceFailure
640
    {
641
        //HACK: change this eventually.  we should not be converting the stream to a string
642
        String xml = IOUtils.toString(is);
643
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData);
644
        IdentifierManager im = IdentifierManager.getInstance();
645
        if(guid != null)
646
        {
647
          im.createMapping(guid.getValue(), localId);
648
        }
649
        return localId;
650
    }
651
    
652
    /**
653
     * insert a document, return the id of the document that was inserted
654
     */
655
    private String insertOrUpdateDocument(String xml, Identifier guid, SessionData sessionData, String insertOrUpdate) 
656
        throws ServiceFailure {
657
        logMetacat.debug("Starting to insert xml document...");
658
        IdentifierManager im = IdentifierManager.getInstance();
659

    
660
        // generate guid/localId pair for sysmeta
661
        String localId = null;
662
        if(insertOrUpdate.equals("insert"))
663
        {
664
            localId = im.generateLocalId(guid.getValue(), 1);
665
        }
666
        else
667
        {
668
            //localid should already exist in the identifier table, so just find it
669
            try
670
            {
671
                localId = im.getLocalId(guid.getValue());
672
                //increment the revision
673
                String docid = localId.substring(0, localId.lastIndexOf("."));
674
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
675
                int rev = new Integer(revS).intValue();
676
                rev++;
677
                docid = docid + "." + rev;
678
                localId = docid;
679
            }
680
            catch(McdbDocNotFoundException e)
681
            {
682
                throw new ServiceFailure(1030, "CrudService.insertOrUpdateDocument(): " +
683
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
684
            }
685
        }
686
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
687
                localId);
688

    
689
        String[] action = new String[1];
690
        action[0] = insertOrUpdate;
691
        params.put("action", action);
692
        String[] docid = new String[1];
693
        docid[0] = localId;
694
        params.put("docid", docid);
695
        String[] doctext = new String[1];
696
        doctext[0] = xml;
697
        logMetacat.debug(doctext[0]);
698
        params.put("doctext", doctext);
699
        
700
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
701
        // onto output stream, or alternatively, capture that and parse it to 
702
        // generate the right exceptions
703
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
704
        //PrintWriter pw = new PrintWriter(output);
705
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
706
                            null, params, sessionData.getUserName(), sessionData.getGroupNames());
707
        //String outputS = new String(output.toByteArray());
708
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
709
//        if (!(outputS.indexOf("<success>") > 0 && outputS.indexOf(localId) > 0)) {
710
//            throw new ServiceFailure(1190, outputS);
711
//        }
712
        logMetacat.debug("Finsished inserting xml document with id " + localId);
713
        return localId;
714
    }
715
    
716
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta) 
717
        throws ServiceFailure {
718
        IBindingFactory bfact;
719
        ByteArrayOutputStream sysmetaOut = null;
720
        try {
721
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
722
            IMarshallingContext mctx = bfact.createMarshallingContext();
723
            sysmetaOut = new ByteArrayOutputStream();
724
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
725
        } catch (JiBXException e) {
726
            throw new ServiceFailure(1190, "Failed to serialize and insert SystemMetadata: " + e.getMessage());
727
        }
728
        
729
        return sysmetaOut;
730
    }
731
    
732
    public static SystemMetadata deserializeSystemMetadata(InputStream xml) 
733
        throws ServiceFailure {
734
        try {
735
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
736
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
737
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
738
            return sysmeta;
739
        } catch (JiBXException e) {
740
            throw new ServiceFailure(1190, "Failed to serialize and insert SystemMetadata: " + e.getMessage());
741
        }    
742
    }
743
}
    (1-1/1)