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.FileInputStream;
28
import java.io.FileNotFoundException;
29
import java.io.FileOutputStream;
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.OutputStream;
33

    
34
import java.security.NoSuchAlgorithmException;
35

    
36
import java.sql.SQLException;
37

    
38
import java.text.DateFormat;
39

    
40
import java.util.Calendar;
41
import java.util.Date;
42
import java.util.Enumeration;
43
import java.util.Hashtable;
44
import java.util.List;
45
import java.util.TimeZone;
46
import java.util.Timer;
47
import java.util.TimerTask;
48
import java.util.Vector;
49

    
50
import javax.servlet.http.HttpServletRequest;
51

    
52
import javax.xml.parsers.ParserConfigurationException;
53
import javax.xml.xpath.XPathExpressionException;
54

    
55
import org.apache.commons.io.IOUtils;
56
import org.apache.log4j.Logger;
57

    
58
import org.dataone.eml.DataoneEMLParser;
59
import org.dataone.eml.EMLDocument;
60
import org.dataone.eml.EMLDocument.DistributionMetadata;
61

    
62
import org.dataone.service.exceptions.BaseException;
63
import org.dataone.service.exceptions.IdentifierNotUnique;
64
import org.dataone.service.exceptions.InsufficientResources;
65
import org.dataone.service.exceptions.InvalidRequest;
66
import org.dataone.service.exceptions.InvalidSystemMetadata;
67
import org.dataone.service.exceptions.InvalidToken;
68
import org.dataone.service.exceptions.NotAuthorized;
69
import org.dataone.service.exceptions.NotFound;
70
import org.dataone.service.exceptions.NotImplemented;
71
import org.dataone.service.exceptions.ServiceFailure;
72
import org.dataone.service.exceptions.UnsupportedType;
73

    
74
import org.dataone.service.mn.MemberNodeCrud;
75

    
76
import org.dataone.service.types.AuthToken;
77
import org.dataone.service.types.Checksum;
78
import org.dataone.service.types.ChecksumAlgorithm;
79
import org.dataone.service.types.DescribeResponse;
80
import org.dataone.service.types.Event;
81
import org.dataone.service.types.Identifier;
82
import org.dataone.service.types.Log;
83
import org.dataone.service.types.LogEntry;
84
import org.dataone.service.types.NodeReference;
85
import org.dataone.service.types.ObjectFormat;
86
import org.dataone.service.types.ObjectList;
87
import org.dataone.service.types.Principal;
88
import org.dataone.service.types.SystemMetadata;
89
import org.dataone.service.types.util.ServiceTypeUtil;
90

    
91
import org.jibx.runtime.BindingDirectory;
92
import org.jibx.runtime.IBindingFactory;
93
import org.jibx.runtime.IMarshallingContext;
94
import org.jibx.runtime.IUnmarshallingContext;
95
import org.jibx.runtime.JiBXException;
96

    
97
import org.xml.sax.SAXException;
98

    
99
import edu.ucsb.nceas.metacat.AccessionNumber;
100
import edu.ucsb.nceas.metacat.AccessionNumberException;
101
import edu.ucsb.nceas.metacat.DocumentImpl;
102
import edu.ucsb.nceas.metacat.EventLog;
103
import edu.ucsb.nceas.metacat.IdentifierManager;
104
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
105
import edu.ucsb.nceas.metacat.McdbException;
106
import edu.ucsb.nceas.metacat.MetacatHandler;
107
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
108
import edu.ucsb.nceas.metacat.client.rest.MetacatRestClient;
109
import edu.ucsb.nceas.metacat.properties.PropertyService;
110
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
111
import edu.ucsb.nceas.metacat.service.SessionService;
112
import edu.ucsb.nceas.metacat.service.ObjectFormatService;
113
import edu.ucsb.nceas.metacat.util.DocumentUtil;
114
import edu.ucsb.nceas.metacat.util.SessionData;
115

    
116
import edu.ucsb.nceas.utilities.ParseLSIDException;
117
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
118

    
119
/**
120
 * 
121
 * Implements DataONE MemberNode CRUD API for Metacat. 
122
 * 
123
 * @author Matthew Jones
124
 */
125
public class CrudService implements MemberNodeCrud
126
{
127
    private static CrudService crudService = null;
128

    
129
    private MetacatHandler handler;
130
    private Hashtable<String, String[]> params;
131
    private Logger logMetacat = null;
132
    private Logger logCrud = null;
133
    
134
    private String metacatUrl;
135
    
136
    /**
137
     * singleton accessor
138
     */
139
    public static CrudService getInstance() 
140
    {
141
      if(crudService == null)
142
      {
143
        crudService = new CrudService();
144
      }
145
      
146
      return crudService;
147
    }
148
    
149
    /**
150
     * Constructor, private for singleton access
151
     */
152
    private CrudService() {
153
        logMetacat = Logger.getLogger(CrudService.class);
154
        logCrud = Logger.getLogger("DataOneLogger");
155
        try
156
        {
157
            String server = PropertyService.getProperty("server.name");
158
            String port = PropertyService.getProperty("server.httpPort");
159
            String context = PropertyService.getProperty("application.context");
160
            metacatUrl = "http://" + server + ":" + port + "/" + context + "/d1";
161
            logMetacat.debug("Initializing CrudService with url " + metacatUrl);
162
        }
163
        catch(Exception e)
164
        {
165
            logMetacat.error("Could not find servlet url in CrudService: " + e.getMessage());
166
            e.printStackTrace();
167
            throw new RuntimeException("Error getting servlet url in CrudService: " + e.getMessage());
168
        }
169
        
170
        params = new Hashtable<String, String[]>();
171
        handler = new MetacatHandler(new Timer());
172
    }
173
    
174
    /**
175
     * return the context url CrudService is using.
176
     */
177
    public String getContextUrl()
178
    {
179
        return metacatUrl;
180
    }
181
    
182
    /**
183
     * Set the context url that this service uses.  It is normally not necessary
184
     * to call this method unless you are trying to connect to a server other
185
     * than the one in which this service is installed.  Otherwise, this value is
186
     * taken from the metacat.properties file (server.name, server.port, application.context).
187
     */
188
    public void setContextUrl(String url)
189
    {
190
        metacatUrl = url;
191
    }
192
    
193
    /**
194
     * set the params for this service from an HttpServletRequest param list
195
     */
196
    public void setParamsFromRequest(HttpServletRequest request)
197
    {
198
        @SuppressWarnings("unchecked")
199
        Enumeration<String> paramlist = request.getParameterNames();
200
        while (paramlist.hasMoreElements()) {
201
            String name = (String) paramlist.nextElement();
202
            String[] value = (String[])request.getParameterValues(name);
203
            params.put(name, value);
204
        }
205
    }
206
    
207
    /**
208
     * Authenticate against metacat and get a token.
209
     * @param username
210
     * @param password
211
     * @return
212
     * @throws ServiceFailure
213
     */
214
    public AuthToken authenticate(String username, String password)
215
      throws ServiceFailure
216
    {
217
        /* TODO:
218
         * This method is not in the original D1 crud spec.  It is highly
219
         * metacat centric.  Higher level decisions need to be made on authentication
220
         * interfaces for D1 nodes.
221
         */
222
        try
223
        {
224
            MetacatRestClient restClient = new MetacatRestClient(getContextUrl());   
225
            String response = restClient.login(username, password);
226
            String sessionid = restClient.getSessionId();
227
            SessionService sessionService = SessionService.getInstance();
228
            sessionService.registerSession(new SessionData(sessionid, username, new String[0], password, "CrudServiceLogin"));
229
            AuthToken token = new AuthToken(sessionid);
230
            EventLog.getInstance().log(metacatUrl,
231
                    username, null, "authenticate");
232
            logCrud.info("authenticate");
233
            return token;
234
        }
235
        catch(Exception e)
236
        {
237
            throw new ServiceFailure("1620", "Error authenticating with metacat: " + e.getMessage());
238
        }
239
    }
240
    
241
    /**
242
     * set the parameter values needed for this request
243
     */
244
    public void setParameter(String name, String[] value)
245
    {
246
        params.put(name, value);
247
    }
248
    
249
    /**
250
     * Generate SystemMetadata for any object in the object store that does
251
     * not already have it.  SystemMetadata documents themselves, are, of course,
252
     * exempt.  This is a utility method for migration of existing object 
253
     * stores to DataONE where SystemMetadata is required for all objects.  See 
254
     * https://trac.dataone.org/ticket/591
255
     * 
256
     * @param token an authtoken with appropriate permissions to read all 
257
     * documents in the object store.  To work correctly, this should probably
258
     * be an adminstrative credential.
259
     * @throws SQLException 
260
     * @throws AccessionNumberException 
261
     * @throws NoSuchAlgorithmException 
262
     * @throws InvalidRequest 
263
     * @throws NotImplemented 
264
     * @throws NotFound 
265
     * @throws NotAuthorized 
266
     * @throws InvalidToken 
267
     * @throws PropertyNotFoundException 
268
     * @throws McdbDocNotFoundException 
269
     * @throws ServiceFailure 
270
     */
271
    public void generateMissingSystemMetadata(AuthToken token) 
272
    throws ServiceFailure, McdbDocNotFoundException, PropertyNotFoundException, InvalidToken, NotAuthorized, 
273
    NotFound, NotImplemented, InvalidRequest, NoSuchAlgorithmException, AccessionNumberException, SQLException 
274
    {
275
        IdentifierManager im = IdentifierManager.getInstance();
276
        //get the list of ids with no SM
277
        List<String> idList = im.getLocalIdsWithNoSystemMetadata();
278
        for (String localId : idList) { 
279
            //for each id, add a system metadata doc
280
            generateMissingSystemMetadata(token, localId);
281
        }
282
        logCrud.info("generateMissingSystemMetadata(token)");
283
    }
284
    
285
    /**
286
     * Generate SystemMetadata for a particular object with identifier localId.
287
     * This is a utility method for migration of existing objects 
288
     * to DataONE where SystemMetadata is required for all objects.
289
     * 
290
     * @param token an authtoken with appropriate permissions to read all 
291
     *        documents in the object store.  To work correctly, this should
292
     *        be an adminstrative credential.
293
     * @param localId the identifier of the object to be processed
294
     * @throws ServiceFailure 
295
     * @throws SQLException 
296
     * @throws AccessionNumberException 
297
     * @throws NoSuchAlgorithmException 
298
     * @throws InvalidRequest 
299
     * @throws NotImplemented 
300
     * @throws NotFound 
301
     * @throws NotAuthorized 
302
     * @throws InvalidToken 
303
     * @throws PropertyNotFoundException 
304
     * @throws McdbDocNotFoundException 
305
     */
306
    public void generateMissingSystemMetadata(AuthToken token, String localId) 
307
    throws ServiceFailure, McdbDocNotFoundException, PropertyNotFoundException, InvalidToken, NotAuthorized,
308
    NotFound, NotImplemented, InvalidRequest, NoSuchAlgorithmException, AccessionNumberException, SQLException 
309
    {
310
    	logCrud.debug("CrudService.generateMissingSystemMetadata() called.");
311
        logCrud.debug("Creating SystemMetadata for localId " + localId);
312
        SystemMetadata sm = null;
313

    
314
        //generate required system metadata fields from the document
315
        try {
316
        	sm = createSystemMetadata(localId, token);
317
        } catch (IOException e1) {
318
        	e1.printStackTrace();
319
        	ServiceFailure sf = new ServiceFailure("00","IOException in generateMissingSystemMetadata: " +
320
        			e1.getMessage());
321
        	sf.setStackTrace(e1.getStackTrace());
322
        	throw sf;
323
        }
324
        
325
        //insert the systemmetadata object
326
        SessionData sessionData = getSessionData(token);
327
        String smlocalid = insertSystemMetadata(sm, sessionData);
328
        logCrud.debug("setting access on SM doc with localid " + smlocalid);
329
        try {
330
        	handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid, "public", "4", "allow", "allowFirst");
331
        } catch (Exception e) {
332
        	logCrud.debug("Unspecified exception thrown by MetacatHandler.setAccess(): " + e.getMessage());
333
        	logMetacat.error("Could not generate missing systemMetadata: " + e.getMessage());
334
        	logMetacat.error(e.getStackTrace());
335
        	ServiceFailure sf = new ServiceFailure("0","Unspecified exception thrown by MetacatHandler.setAccess(): " + e.getMessage());
336
        	sf.setStackTrace(e.getStackTrace());
337
        	throw sf;
338
        }
339

    
340
        String username = "public";
341
        if (sessionData != null) {
342
        	username = sessionData.getUserName();
343
        }
344
        EventLog.getInstance().log(metacatUrl, username, localId, "generateMissingSystemMetadata");
345

    
346
//        catch (Exception e) { // TODO: Please don't catch Exception -- it masks bad things
347
//            e.printStackTrace();
348
//            logCrud.debug("Exception generating missing system metadata: " + e.getMessage());
349
//            logMetacat.error("Could not generate missing system metadata: " + e.getMessage());
350
//        }
351
        logCrud.info("generateMissingSystemMetadata(token, localId)");
352
    }
353
    
354
    /**
355
     * create an object via the crud interface
356
     */
357
    public Identifier create(AuthToken token, Identifier guid, 
358
            InputStream object, SystemMetadata sysmeta) throws InvalidToken, 
359
            ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType, 
360
            InsufficientResources, InvalidSystemMetadata, NotImplemented {
361
        logMetacat.debug("Starting CrudService.create()...");
362
        
363
        // authenticate & get user info
364
        SessionData sessionData = getSessionData(token);
365
        String username = "public";
366
        String[] groups = null;
367
        if(sessionData != null)
368
        {
369
            username = sessionData.getUserName();
370
            groups = sessionData.getGroupNames();
371
        }
372
        String localId = null;
373

    
374
        if (username == null || username.equals("public"))
375
        {
376
            //TODO: many of the thrown exceptions do not use the correct error codes
377
            //check these against the docs and correct them
378
            throw new NotAuthorized("1100", "User " + username + " is not authorized to create content." +
379
                    "  If you are not logged in, please do so and retry the request.");
380
        }
381
        
382
        // verify that guid == SystemMetadata.getIdentifier()
383
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
384
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
385
            throw new InvalidSystemMetadata("1180", 
386
                "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
387
                sysmeta.getIdentifier().getValue() + ").");
388
        }
389

    
390
        logMetacat.debug("Checking if identifier exists...");
391
        // Check that the identifier does not already exist
392
        IdentifierManager im = IdentifierManager.getInstance();
393
        if (im.identifierExists(guid.getValue())) {
394
            throw new IdentifierNotUnique("1120", 
395
                "GUID is already in use by an existing object.");
396
        }
397

    
398
        // Check if we are handling metadata or data
399
        boolean isScienceMetadata = isScienceMetadata(sysmeta);
400
        
401
        if (isScienceMetadata) {
402
            // CASE METADATA:
403
            try {
404
                //logCrud.debug("CrudService: inserting document with guid " + guid.getValue());
405
                this.insertDocument(object, guid, sessionData);
406
                localId = im.getLocalId(guid.getValue());
407
            } catch (IOException e) {
408
                String msg = "Could not create string from XML stream: " +
409
                    " " + e.getMessage();
410
                logMetacat.debug(msg);
411
                throw new ServiceFailure("1190", msg);
412
            } catch(Exception e) {
413
                String msg = "Unexpected error in CrudService.create: " + e.getMessage();
414
                logMetacat.debug(msg);
415
                throw new ServiceFailure("1190", msg);
416
            }
417
            
418

    
419
        } else {
420
            // DEFAULT CASE: DATA (needs to be checked and completed)
421
            localId = insertDataObject(object, guid, sessionData);
422
            
423
        }
424

    
425
        // For Metadata and Data, insert the system metadata into the object store too
426
        String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
427
        //get the document info.  add any access params for the sysmeta too
428
        //logCrud.debug("looking for access records to add for system " +
429
        //    "metadata who's parent doc's  local id is " + localId);
430
        try
431
        {
432
            Hashtable<String, Object> h = im.getDocumentInfo(localId.substring(0, localId.lastIndexOf(".")));
433
            Vector v = (Vector)h.get("access");
434
            for(int i=0; i<v.size(); i++)
435
            {
436
                @SuppressWarnings("unchecked")
437
                Hashtable<String, String> ah = (Hashtable<String, String>)v.elementAt(i);
438
                String principal = (String)ah.get("principal_name");
439
                String permission = (String)ah.get("permission");
440
                String permissionType = (String)ah.get("permission_type");
441
                String permissionOrder = (String)ah.get("permission_order");
442
                int perm = new Integer(permission).intValue();
443
                //logCrud.debug("found access record for principal " + principal);
444
                //logCrud.debug("permission: " + perm + " perm_type: " + permissionType + 
445
                //    " perm_order: " + permissionOrder);
446
                this.setAccess(token, guid, principal, perm, permissionType, permissionOrder, true);
447
            }
448
        }
449
        catch(Exception e)
450
        {
451
            logMetacat.error("Error setting permissions on System Metadata object " + 
452
                    " with id " + sysMetaLocalId + ": " + e.getMessage());
453
            //TODO: decide if this error should cancel the entire create or
454
            //if it should continue with just a logged error.
455
        }
456
        
457
        
458
        logMetacat.debug("Returning from CrudService.create()");
459
        EventLog.getInstance().log(metacatUrl,
460
                username, localId, "create");
461
        logCrud.info("create D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
462
                ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
463
        return guid;
464
    }
465
    
466
    /**
467
     * update an existing object with a new object.  Change the system metadata
468
     * to reflect the changes and update it as well.
469
     */
470
    public Identifier update(AuthToken token, Identifier guid, 
471
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta) 
472
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
473
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata, 
474
            NotImplemented {
475
    	try
476
    	{
477
    		SessionData sessionData = getSessionData(token);
478
            
479
            //find the old systemmetadata (sm.old) document id (the one linked to obsoletedGuid)
480
            SystemMetadata sm = getSystemMetadata(token, obsoletedGuid);
481
            //change sm.old's obsoletedBy field 
482
            List<Identifier> l = sm.getObsoletedByList();
483
            l.add(guid);
484
            sm.setObsoletedByList(l);
485
            //update sm.old
486
            updateSystemMetadata(sm, sessionData);
487
            
488
            //change the obsoletes field of the new systemMetadata (sm.new) to point to the id of the old one
489
            sysmeta.addObsolete(obsoletedGuid);
490
            //insert sm.new
491
            String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
492
            String localId;
493
            
494
            boolean isScienceMetadata = isScienceMetadata(sysmeta);
495
            if(isScienceMetadata)
496
            {
497
                //update the doc
498
                localId = updateDocument(object, obsoletedGuid, guid, sessionData, false);
499
            }
500
            else
501
            {
502
                //update a data file, not xml
503
                localId = insertDataObject(object, guid, sessionData);
504
            }
505
            
506
            IdentifierManager im = IdentifierManager.getInstance();
507
            String username = "public";
508
            if(sessionData != null)
509
            {
510
                username = sessionData.getUserName();
511
            }
512
            EventLog.getInstance().log(metacatUrl,
513
                    username, im.getLocalId(guid.getValue()), "update");
514
            logCrud.info("update D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
515
                    ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
516
            return guid;
517
        }
518

    
519
    	catch(IOException e) {
520
            throw new ServiceFailure("1310", "Error updating document in CrudService: " + e.getMessage());
521
        }
522
    	catch( McdbDocNotFoundException e) {
523
    		throw new ServiceFailure("1310", "Error updating document in CrudService: " + e.getMessage());
524
    	}
525
    	catch( InvalidRequest e) {
526
    		throw new ServiceFailure("1310", "Error updating document in CrudService: " + e.getMessage());
527
    	}
528
    }
529
    
530
    /**
531
     * set access permissions on both the science metadata and system metadata
532
     */
533
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
534
            String permissionType, String permissionOrder)
535
      throws ServiceFailure
536
    {
537
        setAccess(token, id, principal, permission, permissionType, permissionOrder, true);
538
    }
539
    
540
    /**
541
     * set access control on the doc
542
     * @param token
543
     * @param id
544
     * @param principal
545
     * @param permission
546
     */
547
    public void setAccess(AuthToken token, Identifier id, String principal, int permission,
548
      String permissionType, String permissionOrder, boolean setSystemMetadata)
549
      throws ServiceFailure
550
    {
551
        String perm = "";
552
        if(permission >= 4)
553
        {
554
            perm = "read";
555
        }
556
        if(permission >= 6)
557
        {
558
            perm = "write";
559
        }
560
        //logCrud.debug("perm in setAccess: " + perm);
561
        //logCrud.debug("permission in setAccess: " + permission);
562
        setAccess(token, id, principal, perm, permissionType, permissionOrder,
563
                setSystemMetadata);
564
       
565
    }
566
    
567
    /**
568
     * set the permission on the document
569
     * @param token
570
     * @param principal
571
     * @param permission
572
     * @param permissionType
573
     * @param permissionOrder
574
     * @return
575
     */
576
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
577
            String permissionType, String permissionOrder, boolean setSystemMetadata)
578
      throws ServiceFailure
579
    {
580
        /* TODO:
581
         * This is also not part of the D1 Crud spec.  This method is needed for
582
         * systems such as metacat where access to objects is controlled by
583
         * and ACL.  Higher level decisions need to be made about how this
584
         * should work within D1.
585
         */
586
        try
587
        {
588
            final SessionData sessionData = getSessionData(token);
589
            if(sessionData == null)
590
            {
591
                throw new ServiceFailure("1000", "User must be logged in to set access.");
592
            }
593
            IdentifierManager im = IdentifierManager.getInstance();
594
            String docid = im.getLocalId(id.getValue());
595
        
596
            String permNum = "0";
597
            if(permission.equals("read"))
598
            {
599
                permNum = "4";
600
            }
601
            else if(permission.equals("write"))
602
            {
603
                permNum = "6";
604
            }
605
            logCrud.debug("user " + sessionData.getUserName() + 
606
                    " is setting access level " + permNum + " for permission " + 
607
                    permissionType + " on doc with localid " + docid);
608
            handler.setAccess(metacatUrl, sessionData.getUserName(), docid, 
609
                    principal, permNum, permissionType, permissionOrder);
610
            if(setSystemMetadata)
611
            {
612
                //set the same perms on the system metadata doc
613
                String smlocalid = im.getSystemMetadataLocalId(id.getValue());
614
                logCrud.debug("setting access on SM doc with localid " + smlocalid);
615
                //cs.setAccess(token, smid, principal, permission, permissionType, permissionOrder);
616
                handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid,
617
                        principal, permNum, permissionType, permissionOrder);
618
            }
619
            String username = "public";
620
            if(sessionData != null)
621
            {
622
                username = sessionData.getUserName();
623
            }
624
            EventLog.getInstance().log(metacatUrl,
625
                    username, im.getLocalId(id.getValue()), "setAccess");
626
            logCrud.info("setAccess");
627
        }
628
        catch(Exception e)
629
        {
630
            e.printStackTrace();
631
            throw new ServiceFailure("1000", "Could not set access on the document with id " + id.getValue());
632
        }
633
    }
634
    
635
    /**
636
     *  Retrieve the list of objects present on the MN that match the calling 
637
     *  parameters. This method is required to support the process of Member 
638
     *  Node synchronization. At a minimum, this method should be able to 
639
     *  return a list of objects that match:
640
     *  startTime <= SystemMetadata.dateSysMetadataModified
641
     *  but is expected to also support date range (by also specifying endTime), 
642
     *  and should also support slicing of the matching set of records by 
643
     *  indicating the starting index of the response (where 0 is the index 
644
     *  of the first item) and the count of elements to be returned.
645
     *  
646
     *  If startTime or endTime is null, the query is not restricted by that parameter.
647
     *  
648
     * @see http://mule1.dataone.org/ArchitectureDocs/mn_api_replication.html#MN_replication.listObjects
649
     * @param token
650
     * @param startTime
651
     * @param endTime
652
     * @param objectFormat
653
     * @param replicaStatus
654
     * @param start
655
     * @param count
656
     * @return ObjectList
657
     * @throws NotAuthorized
658
     * @throws InvalidRequest
659
     * @throws NotImplemented
660
     * @throws ServiceFailure
661
     * @throws InvalidToken
662
     */
663
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
664
        ObjectFormat objectFormat, boolean replicaStatus, int start, int count)
665
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
666
    {
667
      //ObjectList ol = new ObjectList();
668
      //final SessionData sessionData = getSessionData(token);
669
      //int totalAfterQuery = 0;
670
      
671
      
672
      return IdentifierManager.getInstance().querySystemMetadata(startTime, endTime,
673
              objectFormat, replicaStatus, start, count);
674
      
675
      
676
      /////////////////////////////////////////////////////////////////////////
677
      ///////////////// OLD CODE //////////////////////////////////////////////
678
      /////////////////////////////////////////////////////////////////////////
679
      
680
//      try
681
//      {
682
//          if (PropertyService.getProperty("database.queryCacheOn").equals("true"))
683
//          {
684
//              //System.out.println("the string stored into cache is "+ resultsetBuffer.toString());
685
//              DBQuery.clearQueryResultCache();
686
//          }
687
//      }
688
//      catch (PropertyNotFoundException e1)
689
//      {
690
//          //just don't do anything
691
//      }
692
//      
693
//      try
694
//      {
695
//          //TODO: Milliseconds need to be added to the dateFormat
696
//          System.out.println("=========== Listing Objects =============");
697
//          System.out.println("Current server time is: " + new Date());
698
//          if(startTime != null)
699
//          {
700
//              System.out.println("query start time is " + startTime);
701
//          }
702
//          if(endTime != null)
703
//          {
704
//              System.out.println("query end time is " + endTime);
705
//          }
706
//          params.clear();
707
//          params.put("qformat", new String[] {PropertyService.getProperty("crudService.listObjects.QFormat")});
708
//          params.put("action", new String[] {"squery"});
709
//          params.put("query", new String[] {createListObjectsPathQueryDocument()});
710
//          
711
//          /*System.out.println("query is: metacatUrl: " + metacatUrl + " user: " + sessionData.getUserName() +
712
//                  " sessionid: " + sessionData.getId() + " params: ");
713
//          String url = metacatUrl + "/metacat?action=query&sessionid=" + sessionData.getId();
714
//          Enumeration keys = params.keys();
715
//          while(keys.hasMoreElements())
716
//          {
717
//              String key = (String)keys.nextElement();
718
//              String[] parr = params.get(key);
719
//              for(int i=0; i<parr.length; i++)
720
//              {
721
//                  System.out.println("param " + key + ": " + parr[i]);
722
//                  url += "&" + key + "=" + parr[i] ;
723
//              }
724
//          }
725
//          System.out.println("query url: " + url);
726
//          */
727
//          String username = "public";
728
//          String[] groups = null;
729
//          String sessionid = "";
730
//          if(sessionData != null)
731
//          {
732
//              username = sessionData.getUserName();
733
//              groups = sessionData.getGroupNames();
734
//              sessionid = sessionData.getId();
735
//          }
736
//          
737
//          MetacatResultSet rs = handler.query(metacatUrl, params, username, 
738
//                  groups, sessionid);
739
//          List docs = rs.getDocuments();
740
//          
741
//          System.out.println("query returned " + docs.size() + " documents.");
742
//          Vector<Document> docCopy = new Vector<Document>();
743
//          
744
//          //preparse the list to remove any that don't match the query params
745
//          /* TODO: this type of query/subquery processing is probably not scalable
746
//           * to larger object stores.  This code should be revisited.  The metacat
747
//           * query handler should probably be altered to handle the type of query 
748
//           * done here.
749
//           */ 
750
//          for(int i=0; i<docs.size(); i++)
751
//          {
752
//              Document d = (Document)docs.get(i);
753
//              
754
//              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
755
//              
756
//              if(returnedObjectFormat != null && 
757
//                 objectFormat != null && 
758
//                 !objectFormat.toString().trim().equals(returnedObjectFormat.toString().trim()))
759
//              { //make sure the objectFormat is the one specified
760
//                  continue;
761
//              }
762
//              
763
//              String dateSMM = d.getField("dateSysMetadataModified");
764
//              if((startTime != null || endTime != null) && dateSMM == null)
765
//              {  //if startTime or endTime are not null, we need a date to compare to
766
//                  continue;
767
//              }
768
//              
769
//              //date parse
770
//              Date dateSysMetadataModified = null;
771
//              if(dateSMM != null)
772
//              {
773
//
774
//                  /*                  
775
//                  if(dateSMM.indexOf(".") != -1)
776
//                  {  //strip the milliseconds
777
//                      //TODO: don't do this. we need milliseconds now.
778
//                      //TODO: explore ISO 8601 to figure out milliseconds
779
//                      dateSMM = dateSMM.substring(0, dateSMM.indexOf(".")) + 'Z';
780
//                  }
781
//                  */
782
//                  //System.out.println("dateSMM: " + dateSMM);
783
//                  //dateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
784
//                  try
785
//                  {   //the format we want
786
//                      dateSysMetadataModified = dateFormat.parse(dateSMM);
787
//                  }
788
//                  catch(java.text.ParseException pe)
789
//                  {   //try another legacy format
790
//                      try
791
//                      {
792
//                          DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.S'Z'");
793
//                          dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
794
//                          dateSysMetadataModified = dateFormat2.parse(dateSMM);
795
//                      }
796
//                      catch(java.text.ParseException pe2)
797
//                      {
798
//                          //try another legacy format
799
//                          DateFormat dateFormat3 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
800
//                          dateFormat3.setTimeZone(TimeZone.getTimeZone("GMT-0"));
801
//                          dateSysMetadataModified = dateFormat3.parse(dateSMM);
802
//                      }
803
//                      
804
//                  }                  
805
//              }
806
//              
807
//              /*System.out.println("====================================");
808
//              System.out.println("doc number " + i);
809
//              System.out.println("docid: " + d.docid);
810
//              System.out.println("guid: " + d.getField("identifier").trim());
811
//              System.out.println("dateSMM: " + dateSMM);
812
//              System.out.println("dateSysMetadataModified: " + dateSysMetadataModified);
813
//              System.out.println("startTime: " + startTime);
814
//              System.out.println("endtime: " + endTime);*/
815
//              
816
//              int startDateComparison = 0;
817
//              int endDateComparison = 0;
818
//              if(startTime != null)
819
//              {
820
//                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
821
//                  zTime.setTime(startTime);
822
//                  startTime = zTime.getTime();
823
//                  
824
//                  if(dateSysMetadataModified == null)
825
//                  {
826
//                      startDateComparison = -1;
827
//                  }
828
//                  else
829
//                  {
830
//                      startDateComparison = dateSysMetadataModified.compareTo(startTime);
831
//                  }
832
//                  //System.out.println("startDateCom: " + startDateComparison);
833
//              }
834
//              else
835
//              {
836
//                  startDateComparison = 1;
837
//              }
838
//              
839
//              if(endTime != null)
840
//              {
841
//                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
842
//                  zTime.setTime(endTime);
843
//                  endTime = zTime.getTime();
844
//                  
845
//                  if(dateSysMetadataModified == null)
846
//                  {
847
//                      endDateComparison = 1;
848
//                  }
849
//                  else
850
//                  {
851
//                      endDateComparison = dateSysMetadataModified.compareTo(endTime);
852
//                  }
853
//                  //System.out.println("endDateCom: " + endDateComparison);
854
//              }
855
//              else
856
//              {
857
//                  endDateComparison = -1;
858
//              }
859
//              
860
//              
861
//              if(startDateComparison < 0 || endDateComparison > 0)
862
//              { 
863
//                  continue;                  
864
//              }
865
//              
866
//              docCopy.add((Document)docs.get(i));
867
//          } //end pre-parse
868
//          
869
//          docs = docCopy;
870
//          totalAfterQuery = docs.size();
871
//          //System.out.println("total after subquery: " + totalAfterQuery);
872
//          
873
//          //make sure we don't run over the end
874
//          int end = start + count;
875
//          if(end > docs.size())
876
//          {
877
//              end = docs.size();
878
//          }
879
//          
880
//          for(int i=start; i<end; i++)
881
//          {
882
//              //get the document from the result
883
//              Document d = (Document)docs.get(i);
884
//              //System.out.println("processing doc " + d.docid);
885
//              
886
//              String dateSMM = d.getField("dateSysMetadataModified");
887
//              //System.out.println("dateSMM: " + dateSMM);
888
//              //System.out.println("parsed date: " + parseDate(dateSMM));
889
//              Date dateSysMetadataModified = null;
890
//              if(dateSMM != null)
891
//              {
892
//                  try
893
//                  {
894
//                      dateSysMetadataModified = parseDate(dateSMM);
895
//                  }
896
//                  catch(Exception e)
897
//                  { //if we fail to parse the date, just ignore the value
898
//                      dateSysMetadataModified = null;
899
//                  }
900
//              }
901
//              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
902
//                
903
//              
904
//              ObjectInfo info = new ObjectInfo();
905
//              //add the fields to the info object
906
//              Checksum cs = new Checksum();
907
//              cs.setValue(d.getField("checksum"));
908
//              String csalg = d.getField("algorithm");
909
//              if(csalg == null)
910
//              {
911
//                  csalg = "MD5";
912
//              }
913
//              ChecksumAlgorithm ca = ChecksumAlgorithm.convert(csalg);
914
//              cs.setAlgorithm(ca);
915
//              info.setChecksum(cs);
916
//              info.setDateSysMetadataModified(dateSysMetadataModified);
917
//              Identifier id = new Identifier();
918
//              id.setValue(d.getField("identifier").trim());
919
//              info.setIdentifier(id);
920
//              info.setObjectFormat(returnedObjectFormat);
921
//              String size = d.getField("size");
922
//              if(size != null)
923
//              {
924
//                  info.setSize(new Long(size.trim()).longValue());
925
//              }
926
//              //add the ObjectInfo to the ObjectList
927
//              //logCrud.info("objectFormat: " + info.getObjectFormat().toString());
928
//              //logCrud.info("id: " + info.getIdentifier().getValue());
929
//              
930
//              if(info.getIdentifier().getValue() != null)
931
//              { //id can be null from tests.  should not happen in production.
932
//                  if((info.getObjectFormat() != null && !info.getObjectFormat().toString().trim().equals("")))
933
//                  { //objectFormat needs to not be null and not be an empty string
934
//                    ol.addObjectInfo(info);
935
//                  }
936
//                  else
937
//                  {
938
//                      logCrud.info("Not adding object with null objectFormat" + info.getIdentifier().getValue().toString());
939
//                  }
940
//              }
941
//             
942
//          }
943
//      }
944
//      catch(Exception e)
945
//      {
946
//          e.printStackTrace();
947
//          logCrud.error("Error creating ObjectList: " + e.getMessage() + " cause: " + e.getCause());
948
//          throw new ServiceFailure("1580", "Error retrieving ObjectList: " + e.getMessage());
949
//      }
950
//      String username = "public";
951
//      if(sessionData != null)
952
//      {
953
//          username = sessionData.getUserName();
954
//      }
955
//      EventLog.getInstance().log(metacatUrl,
956
//              username, null, "read");
957
//      logCrud.info("listObjects");
958
//      if(totalAfterQuery < count)
959
//      {
960
//          count = totalAfterQuery;
961
//      }
962
//      ol.setCount(count);
963
//      ol.setStart(start);
964
//      ol.setTotal(totalAfterQuery);
965
//      return ol;
966
    }
967
    
968
    /**
969
     * Call listObjects with the default values for replicaStatus (true), start (0),
970
     * and count (1000).
971
     * @param token
972
     * @param startTime
973
     * @param endTime
974
     * @param objectFormat
975
     * @return
976
     * @throws NotAuthorized
977
     * @throws InvalidRequest
978
     * @throws NotImplemented
979
     * @throws ServiceFailure
980
     * @throws InvalidToken
981
     */
982
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime, 
983
        ObjectFormat objectFormat)
984
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
985
    {
986
       return listObjects(token, startTime, endTime, objectFormat, true, 0, 1000);
987
    }
988

    
989
    /**
990
     * Delete a document. 
991
     */
992
    public Identifier delete(AuthToken token, Identifier guid)
993
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
994
            NotImplemented, InvalidRequest {
995
        logCrud.info("delete");
996
        
997
        if(token == null || token.getToken().equals("publid"))
998
        {
999
            throw new NotAuthorized("1320", "You must be logged in to delete records.");
1000
        }
1001
        
1002
        if(guid == null || guid.getValue().trim().equals(""))
1003
        {
1004
            throw new InvalidRequest("1322", "No GUID specified in CrudService.delete()");
1005
        }
1006
        final SessionData sessionData = getSessionData(token);
1007
        IdentifierManager manager = IdentifierManager.getInstance();
1008
        
1009
        String docid;
1010
        try
1011
        {
1012
            docid = manager.getLocalId(guid.getValue());
1013
        }
1014
        catch(McdbDocNotFoundException mnfe)
1015
        {
1016
            throw new InvalidRequest("1322", "GUID " + guid + " not found.");
1017
        }
1018
        
1019
        try
1020
        {
1021
            DocumentImpl.delete(docid, sessionData.getUserName(), sessionData.getGroupNames(), null);
1022
        }
1023
        catch(SQLException e)
1024
        {
1025
            throw new ServiceFailure("1350", "Could not delete document: " + e.getMessage());
1026
        }
1027
        catch(McdbDocNotFoundException e)
1028
        {
1029
          throw new ServiceFailure("1350", "Could not delete document: " + e.getMessage());
1030
        }
1031
        catch(InsufficientKarmaException e)
1032
        {
1033
          throw new ServiceFailure("1350", "Could not delete document: " + e.getMessage());
1034
        }
1035
        catch(Exception e)
1036
        {
1037
          throw new ServiceFailure("1350", "Could not delete document: " + e.getMessage());
1038
        }
1039
        return guid;
1040
    }
1041

    
1042
    /**
1043
     * describe a document.  
1044
     */
1045
    public DescribeResponse describe(AuthToken token, Identifier guid)
1046
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1047
            NotImplemented, InvalidRequest {
1048
        logCrud.info("describe");
1049
        
1050
        if(token == null)
1051
        {
1052
            throw new InvalidToken("1370", "Authentication token is null");
1053
        }
1054
        
1055
        if(guid == null || guid.getValue().trim().equals(""))
1056
        {
1057
            throw new InvalidRequest("1362", "Guid is null.  A valid guid is required.");
1058
        }
1059
        
1060
        SystemMetadata sm = getSystemMetadata(token, guid);
1061
        DescribeResponse dr = new DescribeResponse(sm.getObjectFormat(), 
1062
                sm.getSize(), sm.getDateSysMetadataModified(), sm.getChecksum());
1063
        return dr;
1064
    }
1065
    
1066
    /**
1067
     * get a document with a specified guid.
1068
     */
1069
    public InputStream get(AuthToken token, Identifier guid)
1070
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1071
            NotImplemented {
1072
        
1073
        // Retrieve the session information from the AuthToken
1074
        // If the session is expired, then the user is 'public'
1075
        if(token == null)
1076
        {
1077
            token = new AuthToken("Public");
1078
        }
1079
        final SessionData sessionData = getSessionData(token);
1080
        
1081
        // Look up the localId for this global identifier
1082
        IdentifierManager im = IdentifierManager.getInstance();
1083
        
1084
        try 
1085
        {
1086
            final String localId = im.getLocalId(guid.getValue());
1087
            InputStream objectStream;
1088
            try 
1089
            {
1090
                String username = "public";
1091
                String[] groups = new String[0];
1092
                if(sessionData != null)
1093
                {
1094
                    username = sessionData.getUserName();
1095
                    groups = sessionData.getGroupNames();
1096
                }
1097
                
1098
                objectStream = readFromMetacat(localId, username, groups);
1099
                
1100
            } catch (PropertyNotFoundException e) {
1101
                e.printStackTrace();
1102
                throw new ServiceFailure("1030", "Error getting property from metacat: " + e.getMessage());
1103
            } catch (ClassNotFoundException e) {
1104
                e.printStackTrace();
1105
                throw new ServiceFailure("1030", "Class not found error when reading from metacat: " + e.getMessage());
1106
            } catch (IOException e) {
1107
                e.printStackTrace();
1108
                throw new ServiceFailure("1030", "IOException while reading from metacat: " + e.getMessage());
1109
            } catch (SQLException e) {
1110
                e.printStackTrace();
1111
                throw new ServiceFailure("1030", "SQLException while reading from metacat: " + e.getMessage());
1112
            } catch (McdbException e) {
1113
                e.printStackTrace();
1114
                throw new ServiceFailure("1030", "Metacat DB exception while reading from metacat: " + e.getMessage());
1115
            } catch (ParseLSIDException e) {
1116
                e.printStackTrace();
1117
                throw new NotFound("1020", "LSID parsing exception while reading from metacat: " + e.getMessage());
1118
            } catch (InsufficientKarmaException e) {
1119
                e.printStackTrace();
1120
                throw new NotAuthorized("1000", "User not authorized for get(): " + e.getMessage());
1121
            }
1122
        
1123
        
1124
            String username = "public";
1125
            if(sessionData != null)
1126
            {
1127
                username = sessionData.getUserName();
1128
            }
1129
            
1130
            EventLog.getInstance().log(metacatUrl,
1131
                    username, im.getLocalId(guid.getValue()), "read");
1132
            logCrud.info("get D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId + 
1133
                    ":");
1134
            
1135
            return objectStream;
1136
        } 
1137
        catch (McdbDocNotFoundException e) 
1138
        {
1139
            throw new NotFound("1020", e.getMessage());
1140
        } 
1141
    }
1142

    
1143
    /**
1144
     * get the checksum for a document.  defaults to MD5.
1145
     */
1146
    public Checksum getChecksum(AuthToken token, Identifier guid)
1147
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1148
            InvalidRequest, NotImplemented 
1149
    {
1150
        logCrud.info("getChecksum");
1151
        return getChecksum(token, guid, "MD5");
1152
    }
1153

    
1154
    /**
1155
     * get the checksum for a document with the given algorithm
1156
     */
1157
    public Checksum getChecksum(AuthToken token, Identifier guid, 
1158
            String checksumAlgorithm) throws InvalidToken, ServiceFailure, 
1159
            NotAuthorized, NotFound, InvalidRequest, NotImplemented 
1160
    {
1161
        logCrud.info("getChecksum");
1162
        SystemMetadata sm = getSystemMetadata(token, guid);
1163
        Checksum cs = sm.getChecksum();
1164
        if(cs.getAlgorithm().toString().equals(checksumAlgorithm))
1165
        {
1166
            return cs;
1167
        }
1168
        else
1169
        {
1170
            if(checksumAlgorithm == null)
1171
            {
1172
                checksumAlgorithm = "MD5";
1173
            }
1174
            InputStream docStream = get(token, guid);
1175
            String checksum;
1176
            try
1177
            {
1178
                checksum = checksum(docStream, checksumAlgorithm);
1179
            }
1180
            catch(Exception e)
1181
            {
1182
                throw new ServiceFailure("1410", "Error getting checksum: " + e.getMessage());
1183
            }
1184
            Checksum c = new Checksum();
1185
            c.setAlgorithm(ChecksumAlgorithm.convert(checksumAlgorithm));
1186
            c.setValue(checksum);
1187
            return c;
1188
        }
1189
    }
1190

    
1191
    /**
1192
     * get log records.  
1193
     */
1194
    public Log getLogRecords(AuthToken token, Date fromDate, Date toDate, Event event)
1195
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest, 
1196
            NotImplemented 
1197
    {
1198
        /*System.out.println("=================== Getting log records ===================");
1199
        System.out.println("Current server time is: " + new Date());
1200
        if(fromDate != null)
1201
        {
1202
          System.out.println("query start time is " + fromDate);
1203
        }
1204
        if(toDate != null)
1205
        {
1206
          System.out.println("query end time is " + toDate);
1207
        }*/
1208
        Log log = new Log();
1209
        Vector<LogEntry> logs = new Vector<LogEntry>();
1210
        IdentifierManager im = IdentifierManager.getInstance();
1211
        EventLog el = EventLog.getInstance();
1212
        if(fromDate == null)
1213
        {
1214
            //System.out.println("setting fromdate from null");
1215
            fromDate = new Date(1);
1216
        }
1217
        if(toDate == null)
1218
        {
1219
            //System.out.println("setting todate from null");
1220
            toDate = new Date();
1221
        }
1222
        
1223
        //System.out.println("fromDate: " + fromDate);
1224
        //System.out.println("toDate: " + toDate);
1225
        
1226
        String report = el.getReport(null, null, null, null, 
1227
                new java.sql.Timestamp(fromDate.getTime()), 
1228
                new java.sql.Timestamp(toDate.getTime()), false);
1229
        
1230
        //System.out.println("report: " + report);
1231
        
1232
        String logEntry = "<logEntry>";
1233
        String endLogEntry = "</logEntry>";
1234
        int startIndex = 0;
1235
        int foundIndex = report.indexOf(logEntry, startIndex);
1236
        while(foundIndex != -1)
1237
        {
1238
            //parse out each entry
1239
            int endEntryIndex = report.indexOf(endLogEntry, foundIndex);
1240
            String entry = report.substring(foundIndex, endEntryIndex);
1241
            //System.out.println("entry: " + entry);
1242
            startIndex = endEntryIndex + endLogEntry.length();
1243
            foundIndex = report.indexOf(logEntry, startIndex);
1244
            
1245
            String entryId = getLogEntryField("entryid", entry);
1246
            String ipAddress = getLogEntryField("ipAddress", entry);
1247
            String principal = getLogEntryField("principal", entry);
1248
            String docid = getLogEntryField("docid", entry);
1249
            String eventS = getLogEntryField("event", entry);
1250
            String dateLogged = getLogEntryField("dateLogged", entry);
1251
            
1252
            LogEntry le = new LogEntry();
1253
            
1254
            Event e = Event.convert(eventS);
1255
            if(e == null)
1256
            { //skip any events that are not Dataone Crud events
1257
                continue;
1258
            }
1259
            le.setEvent(e);
1260
            Identifier entryid = new Identifier();
1261
            entryid.setValue(entryId);
1262
            le.setEntryId(entryid);
1263
            Identifier identifier = new Identifier();
1264
            try
1265
            {
1266
                //System.out.println("converting docid '" + docid + "' to a guid.");
1267
                if(docid == null || docid.trim().equals("") || docid.trim().equals("null"))
1268
                {
1269
                    continue;
1270
                }
1271
                docid = docid.substring(0, docid.lastIndexOf("."));
1272
                identifier.setValue(im.getGUID(docid, im.getLatestRevForLocalId(docid)));
1273
            }
1274
            catch(Exception ex)
1275
            { //try to get the guid, if that doesn't work, just use the local id
1276
                //throw new ServiceFailure("1030", "Error getting guid for localId " + 
1277
                //        docid + ": " + ex.getMessage());\
1278
                
1279
                //skip it if the guid can't be found
1280
                continue;
1281
            }
1282
            
1283
            le.setIdentifier(identifier);
1284
            le.setIpAddress(ipAddress);
1285
            Calendar c = Calendar.getInstance();
1286
            String year = dateLogged.substring(0, 4);
1287
            String month = dateLogged.substring(5, 7);
1288
            String date = dateLogged.substring(8, 10);
1289
            //System.out.println("year: " + year + " month: " + month + " day: " + date);
1290
            c.set(new Integer(year).intValue(), new Integer(month).intValue(), new Integer(date).intValue());
1291
            Date logDate = c.getTime();
1292
            le.setDateLogged(logDate);
1293
            NodeReference memberNode = new NodeReference();
1294
            memberNode.setValue(ipAddress);
1295
            le.setMemberNode(memberNode);
1296
            Principal princ = new Principal();
1297
            princ.setValue(principal);
1298
            le.setPrincipal(princ);
1299
            le.setUserAgent("metacat/RESTService");
1300
            
1301
            if(event == null)
1302
            {
1303
                logs.add(le);
1304
            }
1305
            
1306
            if(event != null &&
1307
               e.toString().toLowerCase().trim().equals(event.toString().toLowerCase().trim()))
1308
            {
1309
              logs.add(le);
1310
            }
1311
        }
1312
        
1313
        log.setLogEntryList(logs);
1314
        logCrud.info("getLogRecords");
1315
        return log;
1316
    }
1317
    
1318
    /**
1319
     * parse a logEntry and get the relavent field from it
1320
     * @param fieldname
1321
     * @param entry
1322
     * @return
1323
     */
1324
    private String getLogEntryField(String fieldname, String entry)
1325
    {
1326
        String begin = "<" + fieldname + ">";
1327
        String end = "</" + fieldname + ">";
1328
        //System.out.println("looking for " + begin + " and " + end + " in entry " + entry);
1329
        String s = entry.substring(entry.indexOf(begin) + begin.length(), entry.indexOf(end));
1330
        //System.out.println("entry " + fieldname + " : " + s);
1331
        return s;
1332
    }
1333

    
1334
    /**
1335
     * get the system metadata for a document with a specified guid.
1336
     */
1337
public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
1338
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound, 
1339
            InvalidRequest, NotImplemented {
1340
        
1341
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
1342
        
1343
        // Retrieve the session information from the AuthToken
1344
        // If the session is expired, then the user is 'public'
1345
        final SessionData sessionData = getSessionData(token);
1346
                
1347
        try {
1348
            IdentifierManager im = IdentifierManager.getInstance();
1349
            final String localId = im.getSystemMetadataLocalId(guid.getValue());
1350
            InputStream objectStream;
1351
            
1352
            try {
1353
                String username = "public";
1354
                String[] groupnames = null;
1355
                if(sessionData != null)
1356
                {
1357
                    username = sessionData.getUserName();
1358
                    groupnames = sessionData.getGroupNames();
1359
                }
1360
                
1361
                objectStream = readFromMetacat(localId, username, groupnames);
1362
                
1363
            } catch (PropertyNotFoundException e) {
1364
                e.printStackTrace();
1365
                throw new ServiceFailure("1090", "Property not found while reading system metadata from metacat: " + e.getMessage());
1366
            } catch (ClassNotFoundException e) {
1367
                e.printStackTrace();
1368
                throw new ServiceFailure("1090", "Class not found while reading system metadata from metacat: " + e.getMessage());
1369
            } catch (IOException e) {
1370
                e.printStackTrace();
1371
                throw new ServiceFailure("1090", "IOException while reading system metadata from metacat: " + e.getMessage());
1372
            } catch (SQLException e) {
1373
                e.printStackTrace();
1374
                throw new ServiceFailure("1090", "SQLException while reading system metadata from metacat: " + e.getMessage());
1375
            } catch (McdbException e) {
1376
                e.printStackTrace();
1377
                throw new ServiceFailure("1090", "Metacat DB Exception while reading system metadata from metacat: " + e.getMessage());
1378
            } catch (ParseLSIDException e) {
1379
                e.printStackTrace();
1380
                throw new NotFound("1060", "Error parsing LSID while reading system metadata from metacat: " + e.getMessage());
1381
            } catch (InsufficientKarmaException e) {
1382
                e.printStackTrace();
1383
                throw new NotAuthorized("1040", "User not authorized for get() on system metadata: " + e.getMessage());
1384
            }
1385
                        
1386
            // Deserialize the xml to create a SystemMetadata object
1387
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
1388
            String username = "public";
1389
            if(sessionData != null)
1390
            {
1391
                username = sessionData.getUserName();
1392
            }
1393
            EventLog.getInstance().log(metacatUrl,
1394
                    username, im.getLocalId(guid.getValue()), "read");
1395
            logCrud.info("getsystemmetadata D1GUID:" + guid.getValue()  + 
1396
                    ":D1SYSMETADATA:"+ localId + ":");
1397
            return sysmeta;
1398
            
1399
        } catch (McdbDocNotFoundException e) {
1400
            //e.printStackTrace();
1401
            throw new NotFound("1040", e.getMessage());
1402
        }                
1403
    }
1404
    
1405
    /**
1406
     * parse the date in the systemMetadata
1407
     * @param s
1408
     * @return
1409
     * @throws Exception
1410
     */
1411
    public Date parseDate(String s)
1412
      throws Exception
1413
    {
1414
        /* TODO:
1415
         * This method should be replaced by a DateFormatter
1416
         */
1417
        Date d = null;
1418
        int tIndex = s.indexOf("T");
1419
        int zIndex = s.indexOf("Z");
1420
        if(tIndex != -1 && zIndex != -1)
1421
        { //parse a date that looks like 2010-05-18T21:12:54.362Z
1422
            //System.out.println("original date: " + s);
1423
            
1424
            String date = s.substring(0, tIndex);
1425
            String year = date.substring(0, date.indexOf("-"));
1426
            String month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
1427
            String day = date.substring(date.lastIndexOf("-") + 1, date.length());
1428
            /*System.out.println("date: " + "year: " + new Integer(year).intValue() + 
1429
                    " month: " + new Integer(month).intValue() + " day: " + 
1430
                    new Integer(day).intValue());
1431
            */
1432
            String time = s.substring(tIndex + 1, zIndex);
1433
            String hour = time.substring(0, time.indexOf(":"));
1434
            String minute = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
1435
            String seconds = "00";
1436
            String milliseconds = "00";
1437
            if(time.indexOf(".") != -1)
1438
            {
1439
                seconds = time.substring(time.lastIndexOf(":") + 1, time.indexOf("."));
1440
                milliseconds = time.substring(time.indexOf(".") + 1, time.length());
1441
            }
1442
            else
1443
            {
1444
                seconds = time.substring(time.lastIndexOf(":") + 1, time.length());
1445
            }
1446
            /*System.out.println("time: " + "hour: " + new Integer(hour).intValue() + 
1447
                    " minute: " + new Integer(minute).intValue() + " seconds: " + 
1448
                    new Integer(seconds).intValue() + " milli: " + 
1449
                    new Integer(milliseconds).intValue());*/
1450
            
1451
            //d = DateFormat.getDateTimeInstance().parse(date + " " + time);
1452
            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0")/*TimeZone.getDefault()*/);
1453
            c.set(new Integer(year).intValue(), new Integer(month).intValue() - 1, 
1454
                  new Integer(day).intValue(), new Integer(hour).intValue(), 
1455
                  new Integer(minute).intValue(), new Integer(seconds).intValue());
1456
            c.set(Calendar.MILLISECOND, new Integer(milliseconds).intValue());
1457
            d = new Date(c.getTimeInMillis());
1458
            //System.out.println("d: " + d);
1459
            return d;
1460
        }
1461
        else
1462
        {  //if it's not in the expected format, try the formatter
1463
            return DateFormat.getDateTimeInstance().parse(s);
1464
        }
1465
    }
1466

    
1467
    /*
1468
     * Look up the information on the session using the token provided in
1469
     * the AuthToken.  The Session should have all relevant user information.
1470
     * If the session has expired or is invalid, the 'public' session will
1471
     * be returned, giving the user anonymous access.
1472
     */
1473
    public static SessionData getSessionData(AuthToken token) {
1474
        SessionData sessionData = null;
1475
        String sessionId = "PUBLIC";
1476
        if (token != null) {
1477
            sessionId = token.getToken();
1478
        }
1479
        
1480
        // if the session id is registered in SessionService, get the
1481
        // SessionData for it. Otherwise, use the public session.
1482
        //System.out.println("sessionid: " + sessionId);
1483
        if (sessionId != null &&
1484
            !sessionId.toLowerCase().equals("public") &&
1485
            SessionService.getInstance().isSessionRegistered(sessionId)) 
1486
        {
1487
            sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
1488
        } else {
1489
            sessionData = SessionService.getInstance().getPublicSession();
1490
        }
1491
        
1492
        return sessionData;
1493
    }
1494

    
1495
    /** 
1496
     * Determine if a given object should be treated as an XML science metadata
1497
     * object. 
1498
     * 
1499
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
1500
     * 
1501
     * @param sysmeta the SystemMetadata describig the object
1502
     * @return true if the object should be treated as science metadata
1503
     */
1504
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
1505
        /*boolean scimeta = false;
1506
        //TODO: this should be read from a .properties file instead of being hard coded
1507
        switch (sysmeta.getObjectFormat()) {
1508
            case EML_2_1_0: scimeta = true; break;
1509
            case EML_2_0_1: scimeta = true; break;
1510
            case EML_2_0_0: scimeta = true; break;
1511
            case FGDC_STD_001_1_1999: scimeta = true; break;
1512
            case FGDC_STD_001_1998: scimeta = true; break;
1513
            case NCML_2_2: scimeta = true; break;
1514
            case DSPACE_METS_SIP_1_0: scimeta = true; break;
1515
        }
1516
        
1517
        return scimeta;*/
1518
        
1519
        return MetadataTypeRegister.isMetadataType(sysmeta.getObjectFormat());
1520
    }
1521

    
1522
    /**
1523
     * insert a data doc
1524
     * @param object
1525
     * @param guid
1526
     * @param sessionData
1527
     * @throws ServiceFailure
1528
     * @returns localId of the data object inserted
1529
     */
1530
    private String insertDataObject(InputStream object, Identifier guid, 
1531
            SessionData sessionData) throws ServiceFailure {
1532
        
1533
        String username = "public";
1534
        String[] groups = null;
1535
        if(sessionData != null)
1536
        {
1537
          username = sessionData.getUserName();
1538
          groups = sessionData.getGroupNames();
1539
        }
1540

    
1541
        // generate guid/localId pair for object
1542
        logMetacat.debug("Generating a guid/localId mapping");
1543
        IdentifierManager im = IdentifierManager.getInstance();
1544
        String localId = im.generateLocalId(guid.getValue(), 1);
1545

    
1546
        try {
1547
            logMetacat.debug("Case DATA: starting to write to disk.");
1548
            if (DocumentImpl.getDataFileLockGrant(localId)) {
1549
    
1550
                // Save the data file to disk using "localId" as the name
1551
                try {
1552
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
1553
    
1554
                    File dataDirectory = new File(datafilepath);
1555
                    dataDirectory.mkdirs();
1556
    
1557
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
1558
    
1559
                    // TODO: Check that the file size matches SystemMetadata
1560
                    //                        long size = newFile.length();
1561
                    //                        if (size == 0) {
1562
                    //                            throw new IOException("Uploaded file is 0 bytes!");
1563
                    //                        }
1564
    
1565
                    // Register the file in the database (which generates an exception
1566
                    // if the localId is not acceptable or other untoward things happen
1567
                    try {
1568
                        logMetacat.debug("Registering document...");
1569
                        DocumentImpl.registerDocument(localId, "BIN", localId,
1570
                                username, groups);
1571
                        logMetacat.debug("Registration step completed.");
1572
                    } catch (SQLException e) {
1573
                        //newFile.delete();
1574
                        logMetacat.debug("SQLE: " + e.getMessage());
1575
                        e.printStackTrace(System.out);
1576
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1577
                    } catch (AccessionNumberException e) {
1578
                        //newFile.delete();
1579
                        logMetacat.debug("ANE: " + e.getMessage());
1580
                        e.printStackTrace(System.out);
1581
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1582
                    } catch (Exception e) {
1583
                        //newFile.delete();
1584
                        logMetacat.debug("Exception: " + e.getMessage());
1585
                        e.printStackTrace(System.out);
1586
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1587
                    }
1588
    
1589
                    logMetacat.debug("Logging the creation event.");
1590
                    EventLog.getInstance().log(metacatUrl,
1591
                            username, localId, "create");
1592
    
1593
                    // Schedule replication for this data file
1594
                    logMetacat.debug("Scheduling replication.");
1595
                    ForceReplicationHandler frh = new ForceReplicationHandler(
1596
                            localId, "create", false, null);
1597
    
1598
                } catch (PropertyNotFoundException e) {
1599
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
1600
                }
1601
            }
1602
            return localId;
1603
        } catch (Exception e) {
1604
            // Could not get a lock on the document, so we can not update the file now
1605
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
1606
        }
1607
    }
1608

    
1609
    /**
1610
     * write a file to a stream
1611
     * @param dir
1612
     * @param fileName
1613
     * @param data
1614
     * @return
1615
     * @throws ServiceFailure
1616
     */
1617
    private File writeStreamToFile(File dir, String fileName, InputStream data) 
1618
        throws ServiceFailure {
1619
        
1620
        File newFile = new File(dir, fileName);
1621
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
1622

    
1623
        try {
1624
            if (newFile.createNewFile()) {
1625
                // write data stream to desired file
1626
                OutputStream os = new FileOutputStream(newFile);
1627
                long length = IOUtils.copyLarge(data, os);
1628
                os.flush();
1629
                os.close();
1630
            } else {
1631
                logMetacat.debug("File creation failed, or file already exists.");
1632
                throw new ServiceFailure("1190", "File already exists: " + fileName);
1633
            }
1634
        } catch (FileNotFoundException e) {
1635
            logMetacat.debug("FNF: " + e.getMessage());
1636
            throw new ServiceFailure("1190", "File not found: " + fileName + " " 
1637
                    + e.getMessage());
1638
        } catch (IOException e) {
1639
            logMetacat.debug("IOE: " + e.getMessage());
1640
            throw new ServiceFailure("1190", "File was not written: " + fileName 
1641
                    + " " + e.getMessage());
1642
        }
1643

    
1644
        return newFile;
1645
    }
1646

    
1647
    /**
1648
     * insert a systemMetadata doc, return the localId of the sysmeta
1649
     */
1650
    private String insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData) 
1651
        throws ServiceFailure 
1652
    {
1653
        logMetacat.debug("Starting to insert SystemMetadata...");
1654
    
1655
        // generate guid/localId pair for sysmeta
1656
        Identifier sysMetaGuid = new Identifier();
1657
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
1658
        sysmeta.setDateSysMetadataModified(new Date());
1659
        logCrud.debug("****inserting new system metadata with modified date " + 
1660
                sysmeta.getDateSysMetadataModified());
1661

    
1662
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
1663
        logCrud.debug("sysmeta: " + xml);
1664
        String localId = insertDocument(xml, sysMetaGuid, sessionData, true);
1665
        logCrud.debug("sysmeta inserted with localId " + localId);
1666
        //insert the system metadata doc id into the systemmetadata table to 
1667
        //link it to the data or metadata document
1668
        IdentifierManager.getInstance().createSystemMetadataMapping(
1669
                sysmeta, sysMetaGuid.getValue());
1670
        return localId;
1671
    }
1672
    
1673
    /**
1674
     * update a systemMetadata doc
1675
     */
1676
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
1677
      throws ServiceFailure
1678
    {
1679
        
1680
        logCrud.debug("CrudService.updateSystemMetadata() called.");
1681
        try
1682
        {
1683
            String smId = IdentifierManager.getInstance().getSystemMetadataLocalId(sm.getIdentifier().getValue());
1684
            logCrud.debug("Setting date modified to " + new Date());
1685
            sm.setDateSysMetadataModified(new Date());
1686
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
1687
            String localId = updateDocument(xml, sm.getIdentifier(), null, sessionData, true);
1688
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
1689
            IdentifierManager.getInstance().insertAdditionalSystemMetadataFields(
1690
              sm.getDateUploaded().getTime(), 
1691
              sm.getRightsHolder().getValue(),
1692
              sm.getChecksum().getValue(), 
1693
              /*sm.getChecksum().getAlgorithm().toString()*/sm.getChecksum().getAlgorithm().name(), 
1694
              sm.getOriginMemberNode().getValue(), 
1695
              sm.getAuthoritativeMemberNode().getValue(), 
1696
              sm.getDateSysMetadataModified().getTime(), 
1697
              sm.getSubmitter().getValue(), 
1698
              sm.getIdentifier().getValue(), 
1699
              sm.getObjectFormat().toString(), 
1700
              sm.getSize());
1701
                        
1702
        }
1703
        catch(Exception e)
1704
        {
1705
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getClass() + ": " + e.getMessage());
1706
        }
1707
    }
1708
    
1709
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
1710
        throws ServiceFailure
1711
    {
1712
        return insertDocument(xml, guid, sessionData, false);
1713
    }
1714
    
1715
    /**
1716
     * insert a document
1717
     * NOTE: this method shouldn't be used from the update or create() methods.  
1718
     * we shouldn't be putting the science metadata or data objects into memory.
1719
     */
1720
    private String insertDocument(String xml, Identifier guid, SessionData sessionData,
1721
            boolean isSystemMetadata)
1722
        throws ServiceFailure
1723
    {
1724
        return insertOrUpdateDocument(xml, guid, sessionData, "insert", isSystemMetadata);
1725
    }
1726
    
1727
    /**
1728
     * insert a document from a stream
1729
     */
1730
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
1731
      throws IOException, ServiceFailure
1732
    {
1733
        //HACK: change this eventually.  we should not be converting the stream to a string
1734
        String xml = IOUtils.toString(is);
1735
        return insertDocument(xml, guid, sessionData);
1736
    }
1737
    
1738
    /**
1739
     * update a document
1740
     * NOTE: this method shouldn't be used from the update or create() methods.  
1741
     * we shouldn't be putting the science metadata or data objects into memory.
1742
     */
1743
    private String updateDocument(String xml, Identifier obsoleteGuid, 
1744
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1745
        throws ServiceFailure
1746
    {
1747
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update", isSystemMetadata);
1748
    }
1749
    
1750
    /**
1751
     * update a document from a stream
1752
     */
1753
    private String updateDocument(InputStream is, Identifier obsoleteGuid, 
1754
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1755
      throws IOException, ServiceFailure
1756
    {
1757
        //HACK: change this eventually.  we should not be converting the stream to a string
1758
        String xml = IOUtils.toString(is);
1759
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData, isSystemMetadata);
1760
        IdentifierManager im = IdentifierManager.getInstance();
1761
        if(guid != null)
1762
        {
1763
          im.createMapping(guid.getValue(), localId);
1764
        }
1765
        return localId;
1766
    }
1767
    
1768
    /**
1769
     * insert a document, return the id of the document that was inserted
1770
     */
1771
    protected String insertOrUpdateDocument(String xml, Identifier guid, 
1772
            SessionData sessionData, String insertOrUpdate, boolean isSystemMetadata) 
1773
        throws ServiceFailure {
1774
        logMetacat.debug("Starting to insert xml document...");
1775
        IdentifierManager im = IdentifierManager.getInstance();
1776

    
1777
        // generate guid/localId pair for sysmeta
1778
        String localId = null;
1779
        if(insertOrUpdate.equals("insert"))
1780
        {
1781
            localId = im.generateLocalId(guid.getValue(), 1, isSystemMetadata);
1782
        }
1783
        else
1784
        {
1785
            //localid should already exist in the identifier table, so just find it
1786
            try
1787
            {
1788
                logCrud.debug("updating guid " + guid.getValue());
1789
                if(!isSystemMetadata)
1790
                {
1791
                    logCrud.debug("looking in identifier table for guid " + guid.getValue());
1792
                    localId = im.getLocalId(guid.getValue());
1793
                }
1794
                else
1795
                {
1796
                    logCrud.debug("looking in systemmetadata table for guid " + guid.getValue());
1797
                    localId = im.getSystemMetadataLocalId(guid.getValue());
1798
                }
1799
                logCrud.debug("localId: " + localId);
1800
                //increment the revision
1801
                String docid = localId.substring(0, localId.lastIndexOf("."));
1802
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
1803
                int rev = new Integer(revS).intValue();
1804
                rev++;
1805
                docid = docid + "." + rev;
1806
                localId = docid;
1807
                logCrud.debug("incremented localId: " + localId);
1808
            }
1809
            catch(McdbDocNotFoundException e)
1810
            {
1811
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
1812
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
1813
            }
1814
        }
1815
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
1816
                localId);
1817

    
1818
        String[] action = new String[1];
1819
        action[0] = insertOrUpdate;
1820
        params.put("action", action);
1821
        String[] docid = new String[1];
1822
        docid[0] = localId;
1823
        params.put("docid", docid);
1824
        String[] doctext = new String[1];
1825
        doctext[0] = xml;
1826
        logMetacat.debug(doctext[0]);
1827
        params.put("doctext", doctext);
1828
        
1829
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
1830
        // onto output stream, or alternatively, capture that and parse it to 
1831
        // generate the right exceptions
1832
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
1833
        //PrintWriter pw = new PrintWriter(output);
1834
        String username = "public";
1835
        String[] groupnames = null;
1836
        if(sessionData != null)
1837
        {
1838
            username = sessionData.getUserName();
1839
            groupnames = sessionData.getGroupNames();
1840
        }
1841
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
1842
                            null, params, username, groupnames);
1843
        if(result.indexOf("<error>") != -1)
1844
        {
1845
            throw new ServiceFailure("1000", "Error inserting or updating document: " + result);
1846
        }
1847
        //String outputS = new String(output.toByteArray());
1848
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
1849
        logMetacat.debug("Finsished inserting xml document with id " + localId);
1850
        return localId;
1851
    }
1852
    
1853
    /**
1854
     * serialize a dataone type
1855
     */
1856
//    private void serializeServiceType(Class type, Object object, OutputStream out)
1857
//        throws JiBXException
1858
//    {
1859
//        IBindingFactory bfact = BindingDirectory.getFactory(type);
1860
//        IMarshallingContext mctx = bfact.createMarshallingContext();
1861
//        mctx.marshalDocument(object, "UTF-8", null, out);
1862
//    }
1863
    
1864
    /**
1865
     * serialize a system metadata doc
1866
     * @param sysmeta
1867
     * @return
1868
     * @throws ServiceFailure
1869
     */
1870
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta) 
1871
        throws ServiceFailure {
1872
        IBindingFactory bfact;
1873
        ByteArrayOutputStream sysmetaOut = null;
1874
        try {
1875
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
1876
            IMarshallingContext mctx = bfact.createMarshallingContext();
1877
            sysmetaOut = new ByteArrayOutputStream();
1878
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
1879
        } catch (JiBXException e) {
1880
            e.printStackTrace();
1881
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
1882
        }
1883
        
1884
        return sysmetaOut;
1885
    }
1886
    
1887
    /**
1888
     * deserialize a system metadata doc
1889
     * @param xml
1890
     * @return
1891
     * @throws ServiceFailure
1892
     */
1893
    public static SystemMetadata deserializeSystemMetadata(InputStream xml) 
1894
        throws ServiceFailure {
1895
        try {
1896
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
1897
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1898
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
1899
            return sysmeta;
1900
        } catch (JiBXException e) {
1901
            e.printStackTrace();
1902
            throw new ServiceFailure("1190", "Failed to deserialize and insert SystemMetadata: " + e.getMessage());
1903
        }    
1904
    }
1905
    
1906
    /**
1907
     * read a document from metacat and return the InputStream
1908
     * 
1909
     * @param localId
1910
     * @param username
1911
     * @param groups
1912
     * @return
1913
     * @throws InsufficientKarmaException
1914
     * @throws ParseLSIDException
1915
     * @throws PropertyNotFoundException
1916
     * @throws McdbException
1917
     * @throws SQLException
1918
     * @throws ClassNotFoundException
1919
     * @throws IOException
1920
     */
1921
    private InputStream readFromMetacat(String localId, String username, String[] groups)
1922
        throws InsufficientKarmaException, ParseLSIDException,
1923
        PropertyNotFoundException, McdbException, SQLException, 
1924
        ClassNotFoundException, IOException
1925
    {
1926
        File tmpDir;
1927
        try
1928
        {
1929
            tmpDir = new File(PropertyService.getProperty("application.tempDir"));
1930
        }
1931
        catch(PropertyNotFoundException pnfe)
1932
        {
1933
            logMetacat.error("ResourceHandler.writeMMPPartstoFiles: " +
1934
                    "application.tmpDir not found.  Using /tmp instead.");
1935
            tmpDir = new File("/tmp");
1936
        }
1937
        Date d = new Date();
1938
        final File outputFile = new File(tmpDir, "metacat.output." + d.getTime());
1939
        FileOutputStream dataSink = new FileOutputStream(outputFile);
1940
        
1941
        handler.readFromMetacat(metacatUrl, null, 
1942
                dataSink, localId, "xml",
1943
                username, 
1944
                groups, true, params);
1945
        
1946
        //set a timer to clean up the temp files
1947
        Timer t = new Timer();
1948
        TimerTask tt = new TimerTask() {
1949
            @Override
1950
            public void run()
1951
            {
1952
                outputFile.delete();
1953
            }
1954
        };
1955
        t.schedule(tt, 20000); //schedule after 20 secs
1956
        
1957
        InputStream objectStream = new FileInputStream(outputFile);
1958
        return objectStream;
1959
    }
1960
    
1961
    /**
1962
     * return an MD5 checksum for the stream
1963
     * @param is
1964
     * @return
1965
     * @throws IOException 
1966
     * @throws NoSuchAlgorithmException 
1967
     */
1968
    public static String checksum(InputStream is) throws NoSuchAlgorithmException, IOException
1969
    {
1970
        return checksum(is, "MD5");
1971
    }
1972
    
1973
    /**
1974
     * produce a checksum for item using the given algorithm
1975
     * @throws IOException 
1976
     * @throws NoSuchAlgorithmException 
1977
     */
1978
    public static String checksum(InputStream is, String algorithm) throws NoSuchAlgorithmException, IOException
1979
    {        
1980
        return ServiceTypeUtil.checksum(is, ChecksumAlgorithm.convert(algorithm)).getValue();
1981
    }
1982
    
1983
    /**
1984
     * parse the metacat date which looks like 2010-06-08 (YYYY-MM-DD) into
1985
     * a proper date object
1986
     * @param date
1987
     * @return
1988
     */
1989
    private Date parseMetacatDate(String date)
1990
    {
1991
        String year = date.substring(0, 4);
1992
        String month = date.substring(5, 7);
1993
        String day = date.substring(8, 10);
1994
        Calendar c = Calendar.getInstance(TimeZone.getDefault());
1995
        c.set(new Integer(year).intValue(), 
1996
              new Integer(month).intValue(), 
1997
              new Integer(day).intValue());
1998
        logCrud.debug("time in parseMetacatDate: " + c.getTime());
1999
        return c.getTime();
2000
    }
2001
    
2002
    /**
2003
     * find the size (in bytes) of a stream
2004
     * @param is
2005
     * @return
2006
     * @throws IOException
2007
     */
2008
    private long sizeOfStream(InputStream is)
2009
        throws IOException
2010
    {
2011
        long size = 0;
2012
        byte[] b = new byte[1024];
2013
        int numread = is.read(b, 0, 1024);
2014
        while(numread != -1)
2015
        {
2016
            size += numread;
2017
            numread = is.read(b, 0, 1024);
2018
        }
2019
        return size;
2020
    }
2021
    
2022
    /**
2023
     * create system metadata with a specified id, doc and format
2024
     * @throws McdbDocNotFoundException 
2025
     * @throws SQLException
2026
     * @throws AccessionNumberException 
2027
     * @throws NumberFormatException 
2028
     * @throws IOException 
2029
     * @throws NoSuchAlgorithmException 
2030
     * @throws PropertyNotFoundException 
2031
     * @throws NotImplemented 
2032
     * @throws NotFound 
2033
     * @throws NotAuthorized 
2034
     * @throws InvalidToken 
2035
     * @throws InvalidRequest 
2036
     * @throws NoSuchAlgorithmException 
2037
     */
2038
    private SystemMetadata createSystemMetadata(String localId, AuthToken token) 
2039
    throws McdbDocNotFoundException, PropertyNotFoundException, AccessionNumberException, SQLException,
2040
    ServiceFailure, InvalidToken, NotAuthorized, NotFound, NotImplemented, InvalidRequest,
2041
    IOException, NoSuchAlgorithmException
2042
    {     
2043
    	logCrud.debug("CrudService.createSystemMetadata() called.");
2044

    
2045
    	IdentifierManager im = IdentifierManager.getInstance();
2046
    	Hashtable<String, Object> docInfo = im.getDocumentInfo(localId);
2047

    
2048
    	//get the document text
2049
    	int rev = im.getLatestRevForLocalId(localId);
2050
    	Identifier identifier = new Identifier();
2051
    	try {
2052
    		identifier.setValue(im.getGUID(localId, rev));
2053

    
2054
    	} catch (McdbDocNotFoundException mcdbe) { 
2055
    		//we're creating a new SM doc for a doc that is not in the identifier table                                       
2056
    		//so we need to add it to
2057
    		logCrud.debug("No guid in the identifier table.  adding it for " + localId);
2058
    		im.createMapping(localId, localId);
2059
    		logCrud.debug("Mapping created for " + localId);
2060
    		AccessionNumber accNum = new AccessionNumber(localId, "NONE");
2061
    		identifier.setValue(im.getGUID(accNum.getDocid(), rev));
2062
    	}
2063

    
2064
    	logCrud.debug("Creating system metadata for guid " + identifier.getValue());
2065
    	InputStream is = this.get(token, identifier);
2066
    	SystemMetadata sm = new SystemMetadata();
2067

    
2068
    	//set the id
2069
    	sm.setIdentifier(identifier);
2070

    
2071
    	//set the default object format
2072
    	String doctype = (String) docInfo.get("doctype");
2073
    	ObjectFormat format = ObjectFormatService.getFormat(doctype);
2074
    	if (format == null) {
2075
    		if (doctype.trim().equals("BIN")) {
2076
    			format = ObjectFormatService.getFormat("application/octet-stream");
2077
    		} else {
2078
    			format = ObjectFormatService.getFormat("text/plain");
2079
    		}
2080
    	}
2081
    	sm.setObjectFormat(format);
2082
    	logCrud.debug("The ObjectFormat for " + localId + " is " + format.toString());
2083

    
2084
    	// further parse EML documents to get data object format,
2085
    	// describes and describedBy information
2086
    	if ( format == ObjectFormatService.getFormat("eml://ecoinformatics.org/eml/2.0.0") ||
2087
    			format == ObjectFormatService.getFormat("eml://ecoinformatics.org/eml/2.0.1") ||
2088
    			format == ObjectFormatService.getFormat("eml://ecoinformatics.org/eml/2.1.0") ) {
2089

    
2090
    		try {
2091
    			DataoneEMLParser emlParser = DataoneEMLParser.getInstance();
2092
    			EMLDocument emlDocument = emlParser.parseDocument(is);
2093

    
2094
    			// iterate through the data objects in the EML doc and add sysmeta
2095
    			logCrud.debug("The number of data entities is: " +
2096
    					emlDocument.distributionMetadata.size());
2097

    
2098
    			for( int j = 0; j < emlDocument.distributionMetadata.size(); j++ ) {
2099

    
2100
    				DistributionMetadata distMetadata = 
2101
    					emlDocument.distributionMetadata.elementAt(j);
2102
    				String dataDocUrl = distMetadata.url;
2103
    				String dataDocMimeType = distMetadata.mimeType;
2104
    				String dataDocLocalId = "";
2105
    				logCrud.debug("\tData local ID: " + dataDocLocalId);
2106
    				logCrud.debug("\tData URL: " + dataDocUrl);
2107
    				logCrud.debug("\tData mime: " + dataDocMimeType);
2108

    
2109
    				//we only handle ecogrid urls right now
2110
    				if ( dataDocUrl.trim().startsWith("ecogrid://knb/") ) {
2111
    					dataDocLocalId = 
2112
    						dataDocUrl.substring(dataDocUrl.indexOf("ecogrid://knb/") + 
2113
    								"ecogrid://knb/".length(), dataDocUrl.length());
2114

    
2115
    					//set the id
2116
    					Identifier dataDocId = new Identifier();
2117
    					dataDocId.setValue(dataDocLocalId);
2118

    
2119
    					// add describes into EML system metadata
2120
    					sm.addDescribe(dataDocId);
2121

    
2122
    					SystemMetadata dataSysMeta = new SystemMetadata();
2123
    					// check if data system metadata exists
2124
    					try {
2125
    						logCrud.debug("Checking for existing system metadata for " + dataDocId.getValue());
2126
    						dataSysMeta = this.getSystemMetadata(token, dataDocId);
2127
    						// add describedBy sysmeta
2128
    						logCrud.debug("Setting describedBy for " + dataDocId.getValue() +
2129
    								" to " + identifier.getValue());
2130
    						dataSysMeta.addDescribedBy(identifier);
2131
    						dataSysMeta.setObjectFormat(ObjectFormatService.getFormat(dataDocMimeType));
2132
    						this.updateSystemMetadata(dataSysMeta, getSessionData(token));
2133

    
2134
    					} catch ( NotFound nf ) {
2135
    						// System metadata for data doesn't exist
2136
    						logCrud.debug("There was not an existing system metadata " + "document for " + dataDocId.getValue());
2137
    						logCrud.debug("Creating a system metadata " + "document for " + dataDocId.getValue());
2138
    						dataSysMeta = this.createSystemMetadata(dataDocLocalId, token);
2139

    
2140
    						logCrud.debug("Setting describedBy for " + dataDocId.getValue() + " to " + identifier.getValue());
2141
    						dataSysMeta.addDescribedBy(identifier);
2142

    
2143
    						logCrud.debug("Setting mimeType for " + dataDocId.getValue() + " to " + dataDocMimeType);
2144
    						dataSysMeta.setObjectFormat(ObjectFormatService.getFormat(dataDocMimeType));
2145

    
2146
    						logCrud.debug("Updating system metadata for " + dataDocId.getValue() + " to " + dataDocMimeType);
2147
    						this.updateSystemMetadata(dataSysMeta, getSessionData(token));
2148
    					}
2149
    				} // end if()
2150
    			} // end for()
2151

    
2152
    		} catch ( ParserConfigurationException pce ) {
2153
    			logCrud.debug("There was a problem parsing the EML document. " +
2154
    					"The error message was: " + pce.getMessage());
2155

    
2156
    		} catch ( SAXException saxe ) {
2157
    			logCrud.debug("There was a problem traversing the EML document. " +
2158
    					"The error message was: " + saxe.getMessage());
2159

    
2160
    		} catch ( XPathExpressionException xpee ) {
2161
    			logCrud.debug("There was a problem searching the EML document. " +
2162
    					"The error message was: " + xpee.getMessage());
2163
    		} // end try
2164

    
2165
    	} // end if()
2166

    
2167
    	//create the checksum
2168
    	is = this.get(token, identifier);
2169
    	String checksumS = checksum(is);
2170
    	ChecksumAlgorithm ca = ChecksumAlgorithm.convert("MD5");
2171
    	Checksum checksum = new Checksum();
2172
    	checksum.setValue(checksumS);
2173
    	checksum.setAlgorithm(ca);
2174
    	sm.setChecksum(checksum);
2175

    
2176
    	//set the size
2177
    	is = this.get(token, identifier);
2178
    	sm.setSize(sizeOfStream(is));
2179

    
2180
    	//submitter
2181
    	Principal p = new Principal();
2182
    	p.setValue((String) docInfo.get("user_owner"));
2183
    	sm.setSubmitter(p);
2184
    	sm.setRightsHolder(p);
2185
    	try {
2186
    		Date dateCreated = parseMetacatDate((String) docInfo.get("date_created"));
2187
    		sm.setDateUploaded(dateCreated);
2188
    		Date dateUpdated = parseMetacatDate((String) docInfo.get("date_updated"));
2189
    		sm.setDateSysMetadataModified(dateUpdated);
2190
    	} catch (Exception e) {
2191
    		logCrud.debug("POSSIBLE ERROR: couldn't parse a date: " + e.getMessage());
2192
    		Date dateCreated = new Date();
2193
    		sm.setDateUploaded(dateCreated);
2194
    		Date dateUpdated = new Date();
2195
    		sm.setDateSysMetadataModified(dateUpdated);
2196
    	}
2197
    	NodeReference nr = new NodeReference();
2198
    	nr.setValue(PropertyService.getProperty("dataone.memberNodeId"));
2199
    	sm.setOriginMemberNode(nr);
2200
    	sm.setAuthoritativeMemberNode(nr);
2201

    
2202
    	// TODO: Need to set describes/describedBy
2203

    
2204
    	return sm;
2205
    }
2206
    
2207
    /**
2208
     * create the listObjects pathQuery document
2209
     */
2210
//    private String createListObjectsPathQueryDocument()
2211
//        throws PropertyNotFoundException
2212
//    {
2213
//        String s = "<pathquery>";
2214
//        s += "<returndoctype>" + PropertyService.getProperty("crudService.listObjects.ReturnDoctype") + "</returndoctype>";
2215
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.1") + "</returnfield>";
2216
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.2") + "</returnfield>";
2217
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.3") + "</returnfield>";
2218
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.4") + "</returnfield>";
2219
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.5") + "</returnfield>";
2220
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.6") + "</returnfield>";
2221
//        s += "<returnfield>" + PropertyService.getProperty("crudService.listObjects.ReturnField.7") + "</returnfield>";
2222
//        s += "<querygroup operator=\"UNION\"><queryterm casesensitive=\"false\" searchmode=\"contains\">";
2223
//        s += "<value>%</value>"; 
2224
//        s += "<pathexpr>" + PropertyService.getProperty("crudService.listObjects.ReturnField.3") + "</pathexpr>";
2225
//        s += "</queryterm></querygroup></pathquery>";
2226
//  
2227
//        return s;
2228
//    }
2229
}
2230

    
(1-1/4)