Project

General

Profile

1 5298 jones
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2000 Regents of the University of California and the
4
 *              National Center for Ecological Analysis and Synthesis
5
 *
6 5299 jones
 *   '$Author: $'
7 5298 jones
 *     '$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 5329 jones
import java.io.ByteArrayOutputStream;
26 5319 jones
import java.io.File;
27 5679 berkley
import java.io.FileInputStream;
28 5319 jones
import java.io.FileNotFoundException;
29
import java.io.FileOutputStream;
30 5298 jones
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.OutputStream;
33 5329 jones
import java.io.PrintWriter;
34 5378 berkley
import java.io.StringBufferInputStream;
35
import java.security.MessageDigest;
36 5298 jones
import java.sql.SQLException;
37 5366 berkley
import java.util.*;
38 5364 berkley
import java.text.DateFormat;
39 5420 berkley
import java.text.SimpleDateFormat;
40 5298 jones
41
import javax.servlet.ServletContext;
42
import javax.servlet.http.HttpServletRequest;
43
import javax.servlet.http.HttpServletResponse;
44
45 5319 jones
import org.apache.commons.io.IOUtils;
46
import org.apache.log4j.Logger;
47 5298 jones
import org.dataone.service.exceptions.IdentifierNotUnique;
48
import org.dataone.service.exceptions.InsufficientResources;
49
import org.dataone.service.exceptions.InvalidRequest;
50
import org.dataone.service.exceptions.InvalidSystemMetadata;
51
import org.dataone.service.exceptions.InvalidToken;
52
import org.dataone.service.exceptions.NotAuthorized;
53
import org.dataone.service.exceptions.NotFound;
54
import org.dataone.service.exceptions.NotImplemented;
55
import org.dataone.service.exceptions.ServiceFailure;
56
import org.dataone.service.exceptions.UnsupportedType;
57
import org.dataone.service.mn.MemberNodeCrud;
58 5364 berkley
import org.dataone.service.types.*;
59 5329 jones
import org.jibx.runtime.BindingDirectory;
60
import org.jibx.runtime.IBindingFactory;
61
import org.jibx.runtime.IMarshallingContext;
62 5332 jones
import org.jibx.runtime.IUnmarshallingContext;
63 5329 jones
import org.jibx.runtime.JiBXException;
64 5298 jones
65 5547 berkley
import org.dataone.service.types.Identifier;
66
67 5298 jones
import com.gc.iotools.stream.is.InputStreamFromOutputStream;
68
69 5798 berkley
import edu.ucsb.nceas.metacat.AccessionNumber;
70 5319 jones
import edu.ucsb.nceas.metacat.AccessionNumberException;
71 5363 berkley
import edu.ucsb.nceas.metacat.MetacatResultSet;
72
import edu.ucsb.nceas.metacat.MetacatResultSet.Document;
73 5510 berkley
import edu.ucsb.nceas.metacat.DBQuery;
74 5319 jones
import edu.ucsb.nceas.metacat.DocumentImpl;
75
import edu.ucsb.nceas.metacat.EventLog;
76 5298 jones
import edu.ucsb.nceas.metacat.IdentifierManager;
77
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
78
import edu.ucsb.nceas.metacat.McdbException;
79
import edu.ucsb.nceas.metacat.MetacatHandler;
80
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
81 5370 berkley
import edu.ucsb.nceas.metacat.client.rest.MetacatRestClient;
82 5319 jones
import edu.ucsb.nceas.metacat.properties.PropertyService;
83
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
84 5298 jones
import edu.ucsb.nceas.metacat.service.SessionService;
85 5329 jones
import edu.ucsb.nceas.metacat.util.DocumentUtil;
86 5298 jones
import edu.ucsb.nceas.metacat.util.SessionData;
87
import edu.ucsb.nceas.utilities.ParseLSIDException;
88
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
89
90
/**
91
 *
92
 * Implements DataONE MemberNode CRUD API for Metacat.
93
 *
94
 * @author Matthew Jones
95
 */
96 5394 berkley
public class CrudService implements MemberNodeCrud
97
{
98 5337 berkley
    private static CrudService crudService = null;
99 5298 jones
100
    private MetacatHandler handler;
101
    private Hashtable<String, String[]> params;
102 5392 berkley
    private Logger logMetacat = null;
103
    private Logger logCrud = null;
104 5337 berkley
105
    private String metacatUrl;
106 5420 berkley
107 5652 berkley
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
108 5298 jones
109
    /**
110 5337 berkley
     * singleton accessor
111
     */
112 5630 berkley
    public static CrudService getInstance()
113 5337 berkley
    {
114
      if(crudService == null)
115
      {
116
        crudService = new CrudService();
117
      }
118
119
      return crudService;
120
    }
121
122
    /**
123 5770 berkley
     * Constructor, private for singleton access
124 5298 jones
     */
125 5763 berkley
    private CrudService() {
126 5337 berkley
        logMetacat = Logger.getLogger(CrudService.class);
127 5392 berkley
        logCrud = Logger.getLogger("DataOneLogger");
128 5337 berkley
        try
129
        {
130
            String server = PropertyService.getProperty("server.name");
131
            String port = PropertyService.getProperty("server.httpPort");
132
            String context = PropertyService.getProperty("application.context");
133 5746 berkley
            metacatUrl = "http://" + server + ":" + port + "/" + context + "/d1";
134 5337 berkley
            logMetacat.debug("Initializing CrudService with url " + metacatUrl);
135
        }
136
        catch(Exception e)
137
        {
138
            logMetacat.error("Could not find servlet url in CrudService: " + e.getMessage());
139
            e.printStackTrace();
140
            throw new RuntimeException("Error getting servlet url in CrudService: " + e.getMessage());
141
        }
142
143
        params = new Hashtable<String, String[]>();
144
        handler = new MetacatHandler(new Timer());
145 5298 jones
    }
146 5337 berkley
147 5298 jones
    /**
148 5337 berkley
     * return the context url CrudService is using.
149 5298 jones
     */
150 5337 berkley
    public String getContextUrl()
151
    {
152
        return metacatUrl;
153
    }
154
155
    /**
156 5394 berkley
     * Set the context url that this service uses.  It is normally not necessary
157
     * to call this method unless you are trying to connect to a server other
158
     * than the one in which this service is installed.  Otherwise, this value is
159
     * taken from the metacat.properties file (server.name, server.port, application.context).
160
     */
161
    public void setContextUrl(String url)
162
    {
163
        metacatUrl = url;
164
    }
165
166
    /**
167 5337 berkley
     * set the params for this service from an HttpServletRequest param list
168
     */
169
    public void setParamsFromRequest(HttpServletRequest request)
170
    {
171 5298 jones
        Enumeration paramlist = request.getParameterNames();
172
        while (paramlist.hasMoreElements()) {
173 5337 berkley
            String name = (String) paramlist.nextElement();
174
            String[] value = (String[])request.getParameterValues(name);
175 5298 jones
            params.put(name, value);
176
        }
177
    }
178
179 5337 berkley
    /**
180 5370 berkley
     * Authenticate against metacat and get a token.
181
     * @param username
182
     * @param password
183
     * @return
184
     * @throws ServiceFailure
185
     */
186
    public AuthToken authenticate(String username, String password)
187
      throws ServiceFailure
188
    {
189 5466 berkley
        /* TODO:
190
         * This method is not in the original D1 crud spec.  It is highly
191
         * metacat centric.  Higher level decisions need to be made on authentication
192
         * interfaces for D1 nodes.
193
         */
194 5370 berkley
        try
195
        {
196
            MetacatRestClient restClient = new MetacatRestClient(getContextUrl());
197
            String response = restClient.login(username, password);
198
            String sessionid = restClient.getSessionId();
199
            SessionService sessionService = SessionService.getInstance();
200
            sessionService.registerSession(new SessionData(sessionid, username, new String[0], password, "CrudServiceLogin"));
201
            AuthToken token = new AuthToken(sessionid);
202 5383 berkley
            EventLog.getInstance().log(metacatUrl,
203 5384 berkley
                    username, null, "authenticate");
204 5436 berkley
            logCrud.info("authenticate");
205 5370 berkley
            return token;
206
        }
207
        catch(Exception e)
208
        {
209 5624 berkley
            throw new ServiceFailure("1620", "Error authenticating with metacat: " + e.getMessage());
210 5370 berkley
        }
211
    }
212
213
    /**
214 5337 berkley
     * set the parameter values needed for this request
215
     */
216
    public void setParameter(String name, String[] value)
217
    {
218
        params.put(name, value);
219
    }
220
221 5377 berkley
    /**
222
     * Generate SystemMetadata for any object in the object store that does
223
     * not already have it.  SystemMetadata documents themselves, are, of course,
224
     * exempt.  This is a utility method for migration of existing object
225
     * stores to DataONE where SystemMetadata is required for all objects.  See
226
     * https://trac.dataone.org/ticket/591
227 5378 berkley
     *
228
     * @param token an authtoken with appropriate permissions to read all
229
     * documents in the object store.  To work correctly, this should probably
230
     * be an adminstrative credential.
231 5377 berkley
     */
232 5378 berkley
    public void generateMissingSystemMetadata(AuthToken token)
233 5377 berkley
    {
234 5378 berkley
        IdentifierManager im = IdentifierManager.getInstance();
235 5377 berkley
        //get the list of ids with no SM
236 5378 berkley
        List<String> l = im.getLocalIdsWithNoSystemMetadata();
237 5377 berkley
        for(int i=0; i<l.size(); i++)
238
        { //for each id, add a system metadata doc
239 5378 berkley
            String localId = l.get(i);
240 5796 berkley
            System.out.println("Creating SystemMetadata for localId " + localId);
241 5378 berkley
            //get the document
242
            try
243
            {
244
                //generate required system metadata fields from the document
245
                SystemMetadata sm = createSystemMetadata(localId, token);
246
                //insert the systemmetadata object
247 5379 berkley
                SessionData sessionData = getSessionData(token);
248 5798 berkley
                String smlocalid = insertSystemMetadata(sm, sessionData);
249
                System.out.println("setting access on SM doc with localid " + smlocalid);
250
                handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid,
251
                        "public", "4", "allow", "allowFirst");
252
253 5431 berkley
                String username = "public";
254
                if(sessionData != null)
255
                {
256
                    username = sessionData.getUserName();
257
                }
258 5383 berkley
                EventLog.getInstance().log(metacatUrl,
259 5384 berkley
                        username, localId, "generateMissingSystemMetadata");
260 5378 berkley
            }
261
            catch(Exception e)
262
            {
263 5798 berkley
                e.printStackTrace();
264 5378 berkley
                System.out.println("Exception generating missing system metadata: " + e.getMessage());
265
                logMetacat.error("Could not generate missing system metadata: " + e.getMessage());
266
            }
267 5377 berkley
        }
268 5392 berkley
        logCrud.info("generateMissingSystemMetadata");
269 5377 berkley
    }
270
271 5378 berkley
    /**
272 5379 berkley
     * create an object via the crud interface
273 5378 berkley
     */
274 5320 jones
    public Identifier create(AuthToken token, Identifier guid,
275 5298 jones
            InputStream object, SystemMetadata sysmeta) throws InvalidToken,
276
            ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType,
277
            InsufficientResources, InvalidSystemMetadata, NotImplemented {
278 5319 jones
        logMetacat.debug("Starting CrudService.create()...");
279
280 5329 jones
        // authenticate & get user info
281
        SessionData sessionData = getSessionData(token);
282 5431 berkley
        String username = "public";
283
        String[] groups = null;
284
        if(sessionData != null)
285
        {
286
            username = sessionData.getUserName();
287
            groups = sessionData.getGroupNames();
288
        }
289 5383 berkley
        String localId = null;
290 5319 jones
291 5376 berkley
        if (username == null || username.equals("public"))
292
        {
293 5466 berkley
            //TODO: many of the thrown exceptions do not use the correct error codes
294
            //check these against the docs and correct them
295 5624 berkley
            throw new NotAuthorized("1100", "User " + username + " is not authorized to create content." +
296 5376 berkley
                    "  If you are not logged in, please do so and retry the request.");
297
        }
298
299 5319 jones
        // verify that guid == SystemMetadata.getIdentifier()
300 5320 jones
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
301 5329 jones
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
302 5356 berkley
            throw new InvalidSystemMetadata("1180",
303 5750 berkley
                "GUID in method call (" + guid.getValue() + ") does not match GUID in system metadata (" +
304
                sysmeta.getIdentifier().getValue() + ").");
305 5329 jones
        }
306 5319 jones
307
        logMetacat.debug("Checking if identifier exists...");
308
        // Check that the identifier does not already exist
309 5436 berkley
        IdentifierManager im = IdentifierManager.getInstance();
310 5320 jones
        if (im.identifierExists(guid.getValue())) {
311 5356 berkley
            throw new IdentifierNotUnique("1120",
312 5319 jones
                "GUID is already in use by an existing object.");
313
        }
314
315 5329 jones
        // Check if we are handling metadata or data
316
        boolean isScienceMetadata = isScienceMetadata(sysmeta);
317 5319 jones
318 5329 jones
        if (isScienceMetadata) {
319 5331 jones
            // CASE METADATA:
320
            try {
321 5466 berkley
                //System.out.println("CrudService: inserting document with guid " + guid.getValue());
322 5350 berkley
                this.insertDocument(object, guid, sessionData);
323 5383 berkley
                localId = im.getLocalId(guid.getValue());
324 5331 jones
            } catch (IOException e) {
325
                String msg = "Could not create string from XML stream: " +
326
                    " " + e.getMessage();
327
                logMetacat.debug(msg);
328 5356 berkley
                throw new ServiceFailure("1190", msg);
329 5383 berkley
            } catch(Exception e) {
330
                String msg = "Unexpected error in CrudService.create: " + e.getMessage();
331
                logMetacat.debug(msg);
332
                throw new ServiceFailure("1190", msg);
333 5331 jones
            }
334 5383 berkley
335 5319 jones
336 5329 jones
        } else {
337 5331 jones
            // DEFAULT CASE: DATA (needs to be checked and completed)
338 5460 berkley
            localId = insertDataObject(object, guid, sessionData);
339 5331 jones
340
        }
341 5319 jones
342 5331 jones
        // For Metadata and Data, insert the system metadata into the object store too
343 5441 berkley
        String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
344
        //get the document info.  add any access params for the sysmeta too
345 5466 berkley
        //System.out.println("looking for access records to add for system " +
346
        //    "metadata who's parent doc's  local id is " + localId);
347 5441 berkley
        try
348
        {
349 5444 berkley
            Hashtable<String, Object> h = im.getDocumentInfo(localId.substring(0, localId.lastIndexOf(".")));
350 5441 berkley
            Vector v = (Vector)h.get("access");
351
            for(int i=0; i<v.size(); i++)
352
            {
353
                Hashtable ah = (Hashtable)v.elementAt(i);
354
                String principal = (String)ah.get("principal_name");
355
                String permission = (String)ah.get("permission");
356
                String permissionType = (String)ah.get("permission_type");
357
                String permissionOrder = (String)ah.get("permission_order");
358 5443 berkley
                int perm = new Integer(permission).intValue();
359 5466 berkley
                //System.out.println("found access record for principal " + principal);
360
                //System.out.println("permission: " + perm + " perm_type: " + permissionType +
361
                //    " perm_order: " + permissionOrder);
362 5444 berkley
                this.setAccess(token, guid, principal, perm, permissionType, permissionOrder, true);
363 5441 berkley
            }
364
        }
365
        catch(Exception e)
366
        {
367
            logMetacat.error("Error setting permissions on System Metadata object " +
368
                    " with id " + sysMetaLocalId + ": " + e.getMessage());
369 5466 berkley
            //TODO: decide if this error should cancel the entire create or
370
            //if it should continue with just a logged error.
371 5441 berkley
        }
372
373
374 5319 jones
        logMetacat.debug("Returning from CrudService.create()");
375 5383 berkley
        EventLog.getInstance().log(metacatUrl,
376 5384 berkley
                username, localId, "create");
377 5459 berkley
        logCrud.info("create D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId +
378 5582 berkley
                ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
379 5319 jones
        return guid;
380 5298 jones
    }
381 5353 berkley
382
    /**
383
     * update an existing object with a new object.  Change the system metadata
384
     * to reflect the changes and update it as well.
385
     */
386
    public Identifier update(AuthToken token, Identifier guid,
387
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta)
388
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
389
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata,
390
            NotImplemented {
391
        try
392
        {
393
            SessionData sessionData = getSessionData(token);
394
395
            //find the old systemmetadata (sm.old) document id (the one linked to obsoletedGuid)
396
            SystemMetadata sm = getSystemMetadata(token, obsoletedGuid);
397
            //change sm.old's obsoletedBy field
398
            List l = sm.getObsoletedByList();
399
            l.add(guid);
400
            sm.setObsoletedByList(l);
401
            //update sm.old
402
            updateSystemMetadata(sm, sessionData);
403
404
            //change the obsoletes field of the new systemMetadata (sm.new) to point to the id of the old one
405
            sysmeta.addObsolete(obsoletedGuid);
406
            //insert sm.new
407 5459 berkley
            String sysMetaLocalId = insertSystemMetadata(sysmeta, sessionData);
408
            String localId;
409 5353 berkley
410
            boolean isScienceMetadata = isScienceMetadata(sysmeta);
411
            if(isScienceMetadata)
412
            {
413
                //update the doc
414 5459 berkley
                localId = updateDocument(object, obsoletedGuid, guid, sessionData, false);
415 5353 berkley
            }
416
            else
417
            {
418
                //update a data file, not xml
419 5459 berkley
                localId = insertDataObject(object, guid, sessionData);
420 5353 berkley
            }
421 5383 berkley
422
            IdentifierManager im = IdentifierManager.getInstance();
423 5431 berkley
            String username = "public";
424
            if(sessionData != null)
425
            {
426
                username = sessionData.getUserName();
427
            }
428 5383 berkley
            EventLog.getInstance().log(metacatUrl,
429 5384 berkley
                    username, im.getLocalId(guid.getValue()), "update");
430 5459 berkley
            logCrud.info("update D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId +
431 5582 berkley
                    ":D1SYSMETADATA:"+ sysMetaLocalId + ":");
432 5353 berkley
            return guid;
433
        }
434
        catch(Exception e)
435
        {
436 5624 berkley
            throw new ServiceFailure("1310", "Error updating document in CrudService: " + e.getMessage());
437 5353 berkley
        }
438
    }
439 5359 berkley
440
    /**
441 5521 berkley
     * set access permissions on both the science metadata and system metadata
442
     */
443
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
444
            String permissionType, String permissionOrder)
445
      throws ServiceFailure
446
    {
447
        setAccess(token, id, principal, permission, permissionType, permissionOrder, true);
448
    }
449
450
    /**
451 5443 berkley
     * set access control on the doc
452
     * @param token
453
     * @param id
454
     * @param principal
455
     * @param permission
456
     */
457
    public void setAccess(AuthToken token, Identifier id, String principal, int permission,
458
      String permissionType, String permissionOrder, boolean setSystemMetadata)
459
      throws ServiceFailure
460
    {
461
        String perm = "";
462 5445 berkley
        if(permission >= 4)
463 5443 berkley
        {
464
            perm = "read";
465
        }
466 5445 berkley
        if(permission >= 6)
467 5443 berkley
        {
468
            perm = "write";
469
        }
470 5466 berkley
        //System.out.println("perm in setAccess: " + perm);
471
        //System.out.println("permission in setAccess: " + permission);
472 5443 berkley
        setAccess(token, id, principal, perm, permissionType, permissionOrder,
473
                setSystemMetadata);
474
475
    }
476
477
    /**
478 5362 berkley
     * set the permission on the document
479
     * @param token
480
     * @param principal
481
     * @param permission
482
     * @param permissionType
483
     * @param permissionOrder
484
     * @return
485
     */
486
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
487 5424 berkley
            String permissionType, String permissionOrder, boolean setSystemMetadata)
488 5362 berkley
      throws ServiceFailure
489
    {
490 5466 berkley
        /* TODO:
491
         * This is also not part of the D1 Crud spec.  This method is needed for
492
         * systems such as metacat where access to objects is controlled by
493
         * and ACL.  Higher level decisions need to be made about how this
494
         * should work within D1.
495
         */
496 5362 berkley
        try
497
        {
498 5449 berkley
            final SessionData sessionData = getSessionData(token);
499
            if(sessionData == null)
500
            {
501
                throw new ServiceFailure("1000", "User must be logged in to set access.");
502
            }
503 5362 berkley
            IdentifierManager im = IdentifierManager.getInstance();
504
            String docid = im.getLocalId(id.getValue());
505 5449 berkley
506 5362 berkley
            String permNum = "0";
507 5370 berkley
            if(permission.equals("read"))
508 5362 berkley
            {
509
                permNum = "4";
510
            }
511 5370 berkley
            else if(permission.equals("write"))
512 5362 berkley
            {
513
                permNum = "6";
514
            }
515 5424 berkley
            System.out.println("user " + sessionData.getUserName() +
516
                    " is setting access level " + permNum + " for permission " +
517
                    permissionType + " on doc with localid " + docid);
518 5362 berkley
            handler.setAccess(metacatUrl, sessionData.getUserName(), docid,
519
                    principal, permNum, permissionType, permissionOrder);
520 5424 berkley
            if(setSystemMetadata)
521
            {
522
                //set the same perms on the system metadata doc
523
                String smlocalid = im.getSystemMetadataLocalId(id.getValue());
524
                System.out.println("setting access on SM doc with localid " + smlocalid);
525
                //cs.setAccess(token, smid, principal, permission, permissionType, permissionOrder);
526
                handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid,
527
                        principal, permNum, permissionType, permissionOrder);
528
            }
529 5431 berkley
            String username = "public";
530
            if(sessionData != null)
531
            {
532
                username = sessionData.getUserName();
533
            }
534 5383 berkley
            EventLog.getInstance().log(metacatUrl,
535 5384 berkley
                    username, im.getLocalId(id.getValue()), "setAccess");
536 5392 berkley
            logCrud.info("setAccess");
537 5362 berkley
        }
538
        catch(Exception e)
539
        {
540 5370 berkley
            e.printStackTrace();
541 5362 berkley
            throw new ServiceFailure("1000", "Could not set access on the document with id " + id.getValue());
542
        }
543
    }
544
545
    /**
546 5359 berkley
     *  Retrieve the list of objects present on the MN that match the calling
547
     *  parameters. This method is required to support the process of Member
548
     *  Node synchronization. At a minimum, this method should be able to
549
     *  return a list of objects that match:
550
     *  startTime <= SystemMetadata.dateSysMetadataModified
551
     *  but is expected to also support date range (by also specifying endTime),
552
     *  and should also support slicing of the matching set of records by
553
     *  indicating the starting index of the response (where 0 is the index
554
     *  of the first item) and the count of elements to be returned.
555
     *
556 5364 berkley
     *  If startTime or endTime is null, the query is not restricted by that parameter.
557
     *
558 5359 berkley
     * @see http://mule1.dataone.org/ArchitectureDocs/mn_api_replication.html#MN_replication.listObjects
559
     * @param token
560
     * @param startTime
561
     * @param endTime
562
     * @param objectFormat
563
     * @param replicaStatus
564
     * @param start
565
     * @param count
566
     * @return ObjectList
567
     * @throws NotAuthorized
568
     * @throws InvalidRequest
569
     * @throws NotImplemented
570
     * @throws ServiceFailure
571
     * @throws InvalidToken
572
     */
573
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime,
574
        ObjectFormat objectFormat, boolean replicaStatus, int start, int count)
575
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
576
    {
577
      ObjectList ol = new ObjectList();
578 5362 berkley
      final SessionData sessionData = getSessionData(token);
579 5413 berkley
      int totalAfterQuery = 0;
580 5510 berkley
581 5362 berkley
      try
582
      {
583 5510 berkley
          if (PropertyService.getProperty("database.queryCacheOn").equals("true"))
584
          {
585
              //System.out.println("the string stored into cache is "+ resultsetBuffer.toString());
586
              DBQuery.clearQueryResultCache();
587
          }
588
      }
589
      catch (PropertyNotFoundException e1)
590
      {
591
          //just don't do anything
592
      }
593
594
      try
595
      {
596 5466 berkley
          //TODO: Milliseconds need to be added to the dateFormat
597 5430 berkley
          System.out.println("=========== Listing Objects =============");
598
          System.out.println("Current server time is: " + new Date());
599
          if(startTime != null)
600
          {
601
              System.out.println("query start time is " + startTime);
602
          }
603
          if(endTime != null)
604
          {
605
              System.out.println("query end time is " + endTime);
606
          }
607 5362 berkley
          params.clear();
608 5622 berkley
          params.put("returndoctype", new String[] {PropertyService.getProperty("crudService.listObjects.ReturnDoctype")});
609
          params.put("qformat", new String[] {PropertyService.getProperty("crudService.listObjects.QFormat")});
610
          params.put("returnfield", new String[] {
611
                  PropertyService.getProperty("crudService.listObjects.ReturnField.1"),
612
                  PropertyService.getProperty("crudService.listObjects.ReturnField.2"),
613
                  PropertyService.getProperty("crudService.listObjects.ReturnField.3"),
614
                  PropertyService.getProperty("crudService.listObjects.ReturnField.4"),
615
                  PropertyService.getProperty("crudService.listObjects.ReturnField.5"),
616
                  PropertyService.getProperty("crudService.listObjects.ReturnField.6"),
617
                  PropertyService.getProperty("crudService.listObjects.ReturnField.7"),
618
                  });
619
          params.put("anyfield", new String[] {PropertyService.getProperty("crudService.listObjects.anyfield")});
620 5363 berkley
621 5630 berkley
          /*System.out.println("query is: metacatUrl: " + metacatUrl + " user: " + sessionData.getUserName() +
622
                  " sessionid: " + sessionData.getId() + " params: ");
623
          String url = metacatUrl + "/metacat?action=query&sessionid=" + sessionData.getId();
624 5622 berkley
          Enumeration keys = params.keys();
625
          while(keys.hasMoreElements())
626
          {
627
              String key = (String)keys.nextElement();
628
              String[] parr = params.get(key);
629
              for(int i=0; i<parr.length; i++)
630
              {
631
                  System.out.println("param " + key + ": " + parr[i]);
632 5630 berkley
                  url += "&" + key + "=" + parr[i] ;
633 5622 berkley
              }
634
          }
635 5630 berkley
          System.out.println("query url: " + url);
636
          */
637 5426 berkley
          String username = "public";
638
          String[] groups = null;
639
          String sessionid = "";
640
          if(sessionData != null)
641
          {
642
              username = sessionData.getUserName();
643
              groups = sessionData.getGroupNames();
644
              sessionid = sessionData.getId();
645
          }
646 5408 berkley
647 5426 berkley
          MetacatResultSet rs = handler.query(metacatUrl, params, username,
648
                  groups, sessionid);
649 5363 berkley
          List docs = rs.getDocuments();
650 5403 berkley
651 5510 berkley
          System.out.println("query returned " + docs.size() + " documents.");
652 5413 berkley
          Vector<Document> docCopy = new Vector<Document>();
653 5408 berkley
654 5413 berkley
          //preparse the list to remove any that don't match the query params
655 5466 berkley
          /* TODO: this type of query/subquery processing is probably not scalable
656
           * to larger object stores.  This code should be revisited.  The metacat
657
           * query handler should probably be altered to handle the type of query
658
           * done here.
659
           */
660 5413 berkley
          for(int i=0; i<docs.size(); i++)
661 5369 berkley
          {
662 5363 berkley
              Document d = (Document)docs.get(i);
663 5630 berkley
664 5364 berkley
              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
665 5413 berkley
666
              if(returnedObjectFormat != null &&
667
                 objectFormat != null &&
668
                 !objectFormat.toString().trim().equals(returnedObjectFormat.toString().trim()))
669 5364 berkley
              { //make sure the objectFormat is the one specified
670
                  continue;
671
              }
672 5384 berkley
673
              String dateSMM = d.getField("dateSysMetadataModified");
674 5413 berkley
              if((startTime != null || endTime != null) && dateSMM == null)
675
              {  //if startTime or endTime are not null, we need a date to compare to
676 5384 berkley
                  continue;
677
              }
678 5413 berkley
679
              //date parse
680
              Date dateSysMetadataModified = null;
681
              if(dateSMM != null)
682
              {
683 5643 berkley
684
                  /*
685 5420 berkley
                  if(dateSMM.indexOf(".") != -1)
686
                  {  //strip the milliseconds
687 5466 berkley
                      //TODO: don't do this. we need milliseconds now.
688 5481 berkley
                      //TODO: explore ISO 8601 to figure out milliseconds
689 5420 berkley
                      dateSMM = dateSMM.substring(0, dateSMM.indexOf(".")) + 'Z';
690
                  }
691 5643 berkley
                  */
692 5420 berkley
                  //System.out.println("dateSMM: " + dateSMM);
693 5428 berkley
                  //dateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
694
                  try
695
                  {   //the format we want
696
                      dateSysMetadataModified = dateFormat.parse(dateSMM);
697
                  }
698
                  catch(java.text.ParseException pe)
699
                  {   //try another legacy format
700 5681 berkley
                      try
701
                      {
702
                          DateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.S'Z'");
703
                          dateFormat2.setTimeZone(TimeZone.getTimeZone("GMT-0"));
704
                          dateSysMetadataModified = dateFormat2.parse(dateSMM);
705
                      }
706
                      catch(java.text.ParseException pe2)
707
                      {
708
                          //try another legacy format
709
                          DateFormat dateFormat3 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
710
                          dateFormat3.setTimeZone(TimeZone.getTimeZone("GMT-0"));
711
                          dateSysMetadataModified = dateFormat3.parse(dateSMM);
712
                      }
713
714 5428 berkley
                  }
715 5413 berkley
              }
716 5428 berkley
717 5510 berkley
              /*System.out.println("====================================");
718
              System.out.println("doc number " + i);
719
              System.out.println("docid: " + d.docid);
720 5630 berkley
              System.out.println("guid: " + d.getField("identifier").trim());
721
              System.out.println("dateSMM: " + dateSMM);
722 5420 berkley
              System.out.println("dateSysMetadataModified: " + dateSysMetadataModified);
723
              System.out.println("startTime: " + startTime);
724
              System.out.println("endtime: " + endTime);*/
725 5427 berkley
726 5364 berkley
              int startDateComparison = 0;
727
              int endDateComparison = 0;
728
              if(startTime != null)
729
              {
730 5427 berkley
                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
731
                  zTime.setTime(startTime);
732
                  startTime = zTime.getTime();
733
734 5413 berkley
                  if(dateSysMetadataModified == null)
735
                  {
736
                      startDateComparison = -1;
737
                  }
738
                  else
739
                  {
740
                      startDateComparison = dateSysMetadataModified.compareTo(startTime);
741
                  }
742
                  //System.out.println("startDateCom: " + startDateComparison);
743 5364 berkley
              }
744 5413 berkley
              else
745
              {
746
                  startDateComparison = 1;
747
              }
748 5364 berkley
749
              if(endTime != null)
750
              {
751 5427 berkley
                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
752
                  zTime.setTime(endTime);
753
                  endTime = zTime.getTime();
754
755 5413 berkley
                  if(dateSysMetadataModified == null)
756
                  {
757
                      endDateComparison = 1;
758
                  }
759
                  else
760
                  {
761
                      endDateComparison = dateSysMetadataModified.compareTo(endTime);
762
                  }
763
                  //System.out.println("endDateCom: " + endDateComparison);
764 5364 berkley
              }
765 5413 berkley
              else
766
              {
767
                  endDateComparison = -1;
768
              }
769 5364 berkley
770 5413 berkley
771 5364 berkley
              if(startDateComparison < 0 || endDateComparison > 0)
772 5413 berkley
              {
773 5364 berkley
                  continue;
774
              }
775
776 5413 berkley
              docCopy.add((Document)docs.get(i));
777
          } //end pre-parse
778
779
          docs = docCopy;
780
          totalAfterQuery = docs.size();
781 5428 berkley
          //System.out.println("total after subquery: " + totalAfterQuery);
782 5413 berkley
783
          //make sure we don't run over the end
784
          int end = start + count;
785
          if(end > docs.size())
786
          {
787
              end = docs.size();
788
          }
789
790
          for(int i=start; i<end; i++)
791
          {
792
              //get the document from the result
793
              Document d = (Document)docs.get(i);
794
              //System.out.println("processing doc " + d.docid);
795
796
              String dateSMM = d.getField("dateSysMetadataModified");
797 5630 berkley
              //System.out.println("dateSMM: " + dateSMM);
798
              //System.out.println("parsed date: " + parseDate(dateSMM));
799 5413 berkley
              Date dateSysMetadataModified = null;
800
              if(dateSMM != null)
801
              {
802
                  try
803
                  {
804
                      dateSysMetadataModified = parseDate(dateSMM);
805
                  }
806
                  catch(Exception e)
807
                  { //if we fail to parse the date, just ignore the value
808
                      dateSysMetadataModified = null;
809
                  }
810
              }
811
              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
812
813
814 5364 berkley
              ObjectInfo info = new ObjectInfo();
815
              //add the fields to the info object
816
              Checksum cs = new Checksum();
817
              cs.setValue(d.getField("checksum"));
818 5370 berkley
              String csalg = d.getField("algorithm");
819
              if(csalg == null)
820
              {
821
                  csalg = "MD5";
822
              }
823
              ChecksumAlgorithm ca = ChecksumAlgorithm.convert(csalg);
824
              cs.setAlgorithm(ca);
825 5364 berkley
              info.setChecksum(cs);
826
              info.setDateSysMetadataModified(dateSysMetadataModified);
827
              Identifier id = new Identifier();
828 5445 berkley
              id.setValue(d.getField("identifier").trim());
829 5364 berkley
              info.setIdentifier(id);
830
              info.setObjectFormat(returnedObjectFormat);
831 5413 berkley
              String size = d.getField("size");
832
              if(size != null)
833
              {
834
                  info.setSize(new Long(size.trim()).longValue());
835
              }
836 5364 berkley
              //add the ObjectInfo to the ObjectList
837 5557 berkley
              //logCrud.info("objectFormat: " + info.getObjectFormat().toString());
838
              //logCrud.info("id: " + info.getIdentifier().getValue());
839
840 5555 berkley
              if(info.getIdentifier().getValue() != null)
841 5414 berkley
              { //id can be null from tests.  should not happen in production.
842 5555 berkley
                  if((info.getObjectFormat() != null && !info.getObjectFormat().toString().trim().equals("")))
843
                  { //objectFormat needs to not be null and not be an empty string
844
                    ol.addObjectInfo(info);
845
                  }
846 5557 berkley
                  else
847
                  {
848 5559 berkley
                      logCrud.info("Not adding object with null objectFormat" + info.getIdentifier().getValue().toString());
849 5557 berkley
                  }
850 5414 berkley
              }
851 5424 berkley
852 5363 berkley
          }
853 5362 berkley
      }
854
      catch(Exception e)
855
      {
856
          e.printStackTrace();
857 5557 berkley
          logCrud.error("Error creating ObjectList: " + e.getMessage() + " cause: " + e.getCause());
858 5362 berkley
          throw new ServiceFailure("1580", "Error retrieving ObjectList: " + e.getMessage());
859
      }
860 5430 berkley
      String username = "public";
861
      if(sessionData != null)
862
      {
863
          username = sessionData.getUserName();
864
      }
865 5383 berkley
      EventLog.getInstance().log(metacatUrl,
866 5384 berkley
              username, null, "read");
867 5392 berkley
      logCrud.info("listObjects");
868 5577 berkley
      if(totalAfterQuery < count)
869
      {
870
          count = totalAfterQuery;
871
      }
872 5413 berkley
      ol.setCount(count);
873
      ol.setStart(start);
874
      ol.setTotal(totalAfterQuery);
875 5359 berkley
      return ol;
876
    }
877
878
    /**
879
     * Call listObjects with the default values for replicaStatus (true), start (0),
880
     * and count (1000).
881
     * @param token
882
     * @param startTime
883
     * @param endTime
884
     * @param objectFormat
885
     * @return
886
     * @throws NotAuthorized
887
     * @throws InvalidRequest
888
     * @throws NotImplemented
889
     * @throws ServiceFailure
890
     * @throws InvalidToken
891
     */
892
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime,
893
        ObjectFormat objectFormat)
894
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
895
    {
896
       return listObjects(token, startTime, endTime, objectFormat, true, 0, 1000);
897
    }
898 5298 jones
899 5359 berkley
    /**
900 5654 berkley
     * Delete a document.
901 5359 berkley
     */
902 5320 jones
    public Identifier delete(AuthToken token, Identifier guid)
903 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
904 5654 berkley
            NotImplemented, InvalidRequest {
905 5392 berkley
        logCrud.info("delete");
906 5654 berkley
907
        if(token == null || token.getToken().equals("publid"))
908
        {
909
            throw new NotAuthorized("1320", "You must be logged in to delete records.");
910
        }
911
912
        if(guid == null || guid.getValue().trim().equals(""))
913
        {
914
            throw new InvalidRequest("1322", "No GUID specified in CrudService.delete()");
915
        }
916
        final SessionData sessionData = getSessionData(token);
917
        IdentifierManager manager = IdentifierManager.getInstance();
918
919
        String docid;
920
        try
921
        {
922
            docid = manager.getLocalId(guid.getValue());
923
        }
924
        catch(McdbDocNotFoundException mnfe)
925
        {
926
            throw new InvalidRequest("1322", "GUID " + guid + " not found.");
927
        }
928
929
        try
930
        {
931
            DocumentImpl.delete(docid, sessionData.getUserName(), sessionData.getGroupNames(), null);
932
        }
933
        catch(Exception e)
934
        {
935
            throw new ServiceFailure("1350", "Could not delete document: " + e.getMessage());
936
        }
937
938
        return guid;
939 5298 jones
    }
940
941 5359 berkley
    /**
942 5648 berkley
     * describe a document.
943 5359 berkley
     */
944 5320 jones
    public DescribeResponse describe(AuthToken token, Identifier guid)
945 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
946 5648 berkley
            NotImplemented, InvalidRequest {
947 5392 berkley
        logCrud.info("describe");
948 5648 berkley
949
        if(token == null)
950
        {
951
            throw new InvalidToken("1370", "Authentication token is null");
952
        }
953
954
        if(guid == null || guid.getValue().trim().equals(""))
955
        {
956
            throw new InvalidRequest("1362", "Guid is null.  A valid guid is required.");
957
        }
958
959
        SystemMetadata sm = getSystemMetadata(token, guid);
960
        DescribeResponse dr = new DescribeResponse(sm.getObjectFormat(),
961
                sm.getSize(), sm.getDateSysMetadataModified(), sm.getChecksum());
962
        return dr;
963 5298 jones
    }
964
965 5359 berkley
    /**
966
     * get a document with a specified guid.
967
     */
968 5320 jones
    public InputStream get(AuthToken token, Identifier guid)
969 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
970
            NotImplemented {
971
972
        // Retrieve the session information from the AuthToken
973
        // If the session is expired, then the user is 'public'
974 5436 berkley
        if(token == null)
975
        {
976
            token = new AuthToken("Public");
977
        }
978 5298 jones
        final SessionData sessionData = getSessionData(token);
979
980
        // Look up the localId for this global identifier
981
        IdentifierManager im = IdentifierManager.getInstance();
982 5683 berkley
983 5679 berkley
        try
984
        {
985 5320 jones
            final String localId = im.getLocalId(guid.getValue());
986 5683 berkley
            InputStream objectStream;
987 5679 berkley
            try
988
            {
989
                String username = "public";
990
                String[] groups = new String[0];
991
                if(sessionData != null)
992
                {
993
                    username = sessionData.getUserName();
994
                    groups = sessionData.getGroupNames();
995
                }
996 5298 jones
997 5683 berkley
                objectStream = readFromMetacat(localId, username, groups);
998 5679 berkley
999
            } catch (PropertyNotFoundException e) {
1000
                e.printStackTrace();
1001
                throw new ServiceFailure("1030", "Error getting property from metacat: " + e.getMessage());
1002
            } catch (ClassNotFoundException e) {
1003
                e.printStackTrace();
1004
                throw new ServiceFailure("1030", "Class not found error when reading from metacat: " + e.getMessage());
1005
            } catch (IOException e) {
1006
                e.printStackTrace();
1007
                throw new ServiceFailure("1030", "IOException while reading from metacat: " + e.getMessage());
1008
            } catch (SQLException e) {
1009
                e.printStackTrace();
1010
                throw new ServiceFailure("1030", "SQLException while reading from metacat: " + e.getMessage());
1011
            } catch (McdbException e) {
1012
                e.printStackTrace();
1013
                throw new ServiceFailure("1030", "Metacat DB exception while reading from metacat: " + e.getMessage());
1014
            } catch (ParseLSIDException e) {
1015
                e.printStackTrace();
1016
                throw new NotFound("1020", "LSID parsing exception while reading from metacat: " + e.getMessage());
1017
            } catch (InsufficientKarmaException e) {
1018
                e.printStackTrace();
1019
                throw new NotAuthorized("1000", "User not authorized for get(): " + e.getMessage());
1020
            }
1021
1022
1023 5431 berkley
            String username = "public";
1024
            if(sessionData != null)
1025
            {
1026
                username = sessionData.getUserName();
1027
            }
1028
1029 5383 berkley
            EventLog.getInstance().log(metacatUrl,
1030 5384 berkley
                    username, im.getLocalId(guid.getValue()), "read");
1031 5459 berkley
            logCrud.info("get D1GUID:" + guid.getValue() + ":D1SCIMETADATA:" + localId +
1032
                    ":");
1033 5680 berkley
1034 5298 jones
            return objectStream;
1035 5679 berkley
        }
1036
        catch (McdbDocNotFoundException e)
1037
        {
1038 5356 berkley
            throw new NotFound("1020", e.getMessage());
1039 5621 berkley
        }
1040 5298 jones
    }
1041
1042 5359 berkley
    /**
1043 5621 berkley
     * get the checksum for a document.  defaults to MD5.
1044 5359 berkley
     */
1045 5320 jones
    public Checksum getChecksum(AuthToken token, Identifier guid)
1046 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
1047 5621 berkley
            InvalidRequest, NotImplemented
1048
    {
1049 5392 berkley
        logCrud.info("getChecksum");
1050 5621 berkley
        return getChecksum(token, guid, "MD5");
1051 5298 jones
    }
1052
1053 5359 berkley
    /**
1054 5621 berkley
     * get the checksum for a document with the given algorithm
1055 5359 berkley
     */
1056 5320 jones
    public Checksum getChecksum(AuthToken token, Identifier guid,
1057 5298 jones
            String checksumAlgorithm) throws InvalidToken, ServiceFailure,
1058 5621 berkley
            NotAuthorized, NotFound, InvalidRequest, NotImplemented
1059
    {
1060 5392 berkley
        logCrud.info("getChecksum");
1061 5670 berkley
        SystemMetadata sm = getSystemMetadata(token, guid);
1062
        Checksum cs = sm.getChecksum();
1063
        if(cs.getAlgorithm().toString().equals(checksumAlgorithm))
1064 5644 berkley
        {
1065 5670 berkley
            return cs;
1066 5644 berkley
        }
1067 5670 berkley
        else
1068 5621 berkley
        {
1069 5670 berkley
            if(checksumAlgorithm == null)
1070
            {
1071
                checksumAlgorithm = "MD5";
1072
            }
1073
            InputStream docStream = get(token, guid);
1074
            String checksum;
1075
            try
1076
            {
1077
                checksum = checksum(docStream, checksumAlgorithm);
1078
            }
1079
            catch(Exception e)
1080
            {
1081
                throw new ServiceFailure("1410", "Error getting checksum: " + e.getMessage());
1082
            }
1083
            Checksum c = new Checksum();
1084
            c.setAlgorithm(ChecksumAlgorithm.convert(checksumAlgorithm));
1085
            c.setValue(checksum);
1086
            return c;
1087 5621 berkley
        }
1088 5298 jones
    }
1089
1090 5359 berkley
    /**
1091 5384 berkley
     * get log records.
1092 5359 berkley
     */
1093 5390 berkley
    public Log getLogRecords(AuthToken token, Date fromDate, Date toDate, Event event)
1094 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
1095 5384 berkley
            NotImplemented
1096
    {
1097 5630 berkley
        /*System.out.println("=================== Getting log records ===================");
1098 5430 berkley
        System.out.println("Current server time is: " + new Date());
1099
        if(fromDate != null)
1100
        {
1101
          System.out.println("query start time is " + fromDate);
1102
        }
1103
        if(toDate != null)
1104
        {
1105
          System.out.println("query end time is " + toDate);
1106 5630 berkley
        }*/
1107 5389 berkley
        Log log = new Log();
1108
        Vector<LogEntry> logs = new Vector<LogEntry>();
1109 5384 berkley
        IdentifierManager im = IdentifierManager.getInstance();
1110
        EventLog el = EventLog.getInstance();
1111 5391 berkley
        if(fromDate == null)
1112
        {
1113 5630 berkley
            //System.out.println("setting fromdate from null");
1114 5391 berkley
            fromDate = new Date(1);
1115
        }
1116
        if(toDate == null)
1117
        {
1118 5630 berkley
            //System.out.println("setting todate from null");
1119 5391 berkley
            toDate = new Date();
1120
        }
1121 5418 berkley
1122 5630 berkley
        //System.out.println("fromDate: " + fromDate);
1123
        //System.out.println("toDate: " + toDate);
1124 5418 berkley
1125 5384 berkley
        String report = el.getReport(null, null, null, null,
1126
                new java.sql.Timestamp(fromDate.getTime()),
1127 5693 leinfelder
                new java.sql.Timestamp(toDate.getTime()), false);
1128 5384 berkley
1129 5418 berkley
        //System.out.println("report: " + report);
1130
1131 5384 berkley
        String logEntry = "<logEntry>";
1132
        String endLogEntry = "</logEntry>";
1133
        int startIndex = 0;
1134
        int foundIndex = report.indexOf(logEntry, startIndex);
1135
        while(foundIndex != -1)
1136
        {
1137
            //parse out each entry
1138
            int endEntryIndex = report.indexOf(endLogEntry, foundIndex);
1139
            String entry = report.substring(foundIndex, endEntryIndex);
1140
            //System.out.println("entry: " + entry);
1141
            startIndex = endEntryIndex + endLogEntry.length();
1142
            foundIndex = report.indexOf(logEntry, startIndex);
1143
1144
            String entryId = getLogEntryField("entryid", entry);
1145
            String ipAddress = getLogEntryField("ipAddress", entry);
1146
            String principal = getLogEntryField("principal", entry);
1147
            String docid = getLogEntryField("docid", entry);
1148 5390 berkley
            String eventS = getLogEntryField("event", entry);
1149 5384 berkley
            String dateLogged = getLogEntryField("dateLogged", entry);
1150
1151 5389 berkley
            LogEntry le = new LogEntry();
1152 5384 berkley
1153 5390 berkley
            Event e = Event.convert(eventS);
1154 5384 berkley
            if(e == null)
1155
            { //skip any events that are not Dataone Crud events
1156
                continue;
1157
            }
1158 5389 berkley
            le.setEvent(e);
1159 5384 berkley
            Identifier entryid = new Identifier();
1160
            entryid.setValue(entryId);
1161 5389 berkley
            le.setEntryId(entryid);
1162 5384 berkley
            Identifier identifier = new Identifier();
1163
            try
1164
            {
1165 5432 berkley
                //System.out.println("converting docid '" + docid + "' to a guid.");
1166 5417 berkley
                if(docid == null || docid.trim().equals("") || docid.trim().equals("null"))
1167
                {
1168
                    continue;
1169
                }
1170 5416 berkley
                docid = docid.substring(0, docid.lastIndexOf("."));
1171 5384 berkley
                identifier.setValue(im.getGUID(docid, im.getLatestRevForLocalId(docid)));
1172
            }
1173
            catch(Exception ex)
1174
            { //try to get the guid, if that doesn't work, just use the local id
1175 5419 berkley
                //throw new ServiceFailure("1030", "Error getting guid for localId " +
1176 5424 berkley
                //        docid + ": " + ex.getMessage());\
1177
1178
                //skip it if the guid can't be found
1179 5419 berkley
                continue;
1180 5384 berkley
            }
1181
1182 5389 berkley
            le.setIdentifier(identifier);
1183
            le.setIpAddress(ipAddress);
1184 5384 berkley
            Calendar c = Calendar.getInstance();
1185
            String year = dateLogged.substring(0, 4);
1186
            String month = dateLogged.substring(5, 7);
1187
            String date = dateLogged.substring(8, 10);
1188
            //System.out.println("year: " + year + " month: " + month + " day: " + date);
1189
            c.set(new Integer(year).intValue(), new Integer(month).intValue(), new Integer(date).intValue());
1190
            Date logDate = c.getTime();
1191 5389 berkley
            le.setDateLogged(logDate);
1192 5384 berkley
            NodeReference memberNode = new NodeReference();
1193
            memberNode.setValue(ipAddress);
1194 5389 berkley
            le.setMemberNode(memberNode);
1195 5384 berkley
            Principal princ = new Principal();
1196
            princ.setValue(principal);
1197 5389 berkley
            le.setPrincipal(princ);
1198
            le.setUserAgent("metacat/RESTService");
1199 5390 berkley
1200
            if(event == null)
1201
            {
1202
                logs.add(le);
1203
            }
1204
1205
            if(event != null &&
1206
               e.toString().toLowerCase().trim().equals(event.toString().toLowerCase().trim()))
1207
            {
1208
              logs.add(le);
1209
            }
1210 5384 berkley
        }
1211
1212 5389 berkley
        log.setLogEntryList(logs);
1213 5392 berkley
        logCrud.info("getLogRecords");
1214 5389 berkley
        return log;
1215 5298 jones
    }
1216 5384 berkley
1217
    /**
1218
     * parse a logEntry and get the relavent field from it
1219
     * @param fieldname
1220
     * @param entry
1221
     * @return
1222
     */
1223
    private String getLogEntryField(String fieldname, String entry)
1224
    {
1225
        String begin = "<" + fieldname + ">";
1226
        String end = "</" + fieldname + ">";
1227
        //System.out.println("looking for " + begin + " and " + end + " in entry " + entry);
1228
        String s = entry.substring(entry.indexOf(begin) + begin.length(), entry.indexOf(end));
1229
        //System.out.println("entry " + fieldname + " : " + s);
1230
        return s;
1231
    }
1232 5298 jones
1233 5359 berkley
    /**
1234
     * get the system metadata for a document with a specified guid.
1235
     */
1236 5481 berkley
public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
1237 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
1238
            InvalidRequest, NotImplemented {
1239 5319 jones
1240 5332 jones
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
1241
1242
        // Retrieve the session information from the AuthToken
1243
        // If the session is expired, then the user is 'public'
1244
        final SessionData sessionData = getSessionData(token);
1245
1246
        try {
1247
            IdentifierManager im = IdentifierManager.getInstance();
1248 5424 berkley
            final String localId = im.getSystemMetadataLocalId(guid.getValue());
1249 5683 berkley
            InputStream objectStream;
1250 5332 jones
1251 5683 berkley
            try {
1252
                String username = "public";
1253
                String[] groupnames = null;
1254
                if(sessionData != null)
1255
                {
1256
                    username = sessionData.getUserName();
1257
                    groupnames = sessionData.getGroupNames();
1258
                }
1259 5332 jones
1260 5683 berkley
                objectStream = readFromMetacat(localId, username, groupnames);
1261
1262
            } catch (PropertyNotFoundException e) {
1263
                e.printStackTrace();
1264
                throw new ServiceFailure("1090", "Property not found while reading system metadata from metacat: " + e.getMessage());
1265
            } catch (ClassNotFoundException e) {
1266
                e.printStackTrace();
1267
                throw new ServiceFailure("1090", "Class not found while reading system metadata from metacat: " + e.getMessage());
1268
            } catch (IOException e) {
1269
                e.printStackTrace();
1270
                throw new ServiceFailure("1090", "IOException while reading system metadata from metacat: " + e.getMessage());
1271
            } catch (SQLException e) {
1272
                e.printStackTrace();
1273
                throw new ServiceFailure("1090", "SQLException while reading system metadata from metacat: " + e.getMessage());
1274
            } catch (McdbException e) {
1275
                e.printStackTrace();
1276
                throw new ServiceFailure("1090", "Metacat DB Exception while reading system metadata from metacat: " + e.getMessage());
1277
            } catch (ParseLSIDException e) {
1278
                e.printStackTrace();
1279
                throw new NotFound("1060", "Error parsing LSID while reading system metadata from metacat: " + e.getMessage());
1280
            } catch (InsufficientKarmaException e) {
1281
                e.printStackTrace();
1282
                throw new NotAuthorized("1040", "User not authorized for get() on system metadata: " + e.getMessage());
1283
            }
1284 5449 berkley
1285 5332 jones
            // Deserialize the xml to create a SystemMetadata object
1286
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
1287 5431 berkley
            String username = "public";
1288
            if(sessionData != null)
1289
            {
1290
                username = sessionData.getUserName();
1291
            }
1292 5383 berkley
            EventLog.getInstance().log(metacatUrl,
1293 5384 berkley
                    username, im.getLocalId(guid.getValue()), "read");
1294 5459 berkley
            logCrud.info("getsystemmetadata D1GUID:" + guid.getValue()  +
1295 5582 berkley
                    ":D1SYSMETADATA:"+ localId + ":");
1296 5332 jones
            return sysmeta;
1297
1298
        } catch (McdbDocNotFoundException e) {
1299 5348 berkley
            //e.printStackTrace();
1300 5624 berkley
            throw new NotFound("1040", e.getMessage());
1301 5332 jones
        }
1302 5298 jones
    }
1303 5366 berkley
1304
    /**
1305
     * parse the date in the systemMetadata
1306
     * @param s
1307
     * @return
1308
     * @throws Exception
1309
     */
1310 5418 berkley
    public Date parseDate(String s)
1311 5366 berkley
      throws Exception
1312
    {
1313 5466 berkley
        /* TODO:
1314
         * This method should be replaced by a DateFormatter
1315
         */
1316 5366 berkley
        Date d = null;
1317
        int tIndex = s.indexOf("T");
1318
        int zIndex = s.indexOf("Z");
1319
        if(tIndex != -1 && zIndex != -1)
1320
        { //parse a date that looks like 2010-05-18T21:12:54.362Z
1321
            //System.out.println("original date: " + s);
1322
1323
            String date = s.substring(0, tIndex);
1324
            String year = date.substring(0, date.indexOf("-"));
1325
            String month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
1326
            String day = date.substring(date.lastIndexOf("-") + 1, date.length());
1327
            /*System.out.println("date: " + "year: " + new Integer(year).intValue() +
1328
                    " month: " + new Integer(month).intValue() + " day: " +
1329
                    new Integer(day).intValue());
1330
            */
1331
            String time = s.substring(tIndex + 1, zIndex);
1332
            String hour = time.substring(0, time.indexOf(":"));
1333
            String minute = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
1334
            String seconds = "00";
1335
            String milliseconds = "00";
1336
            if(time.indexOf(".") != -1)
1337
            {
1338
                seconds = time.substring(time.lastIndexOf(":") + 1, time.indexOf("."));
1339
                milliseconds = time.substring(time.indexOf(".") + 1, time.length());
1340
            }
1341 5418 berkley
            else
1342
            {
1343
                seconds = time.substring(time.lastIndexOf(":") + 1, time.length());
1344
            }
1345 5366 berkley
            /*System.out.println("time: " + "hour: " + new Integer(hour).intValue() +
1346
                    " minute: " + new Integer(minute).intValue() + " seconds: " +
1347
                    new Integer(seconds).intValue() + " milli: " +
1348
                    new Integer(milliseconds).intValue());*/
1349
1350
            //d = DateFormat.getDateTimeInstance().parse(date + " " + time);
1351 5630 berkley
            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0")/*TimeZone.getDefault()*/);
1352 5366 berkley
            c.set(new Integer(year).intValue(), new Integer(month).intValue() - 1,
1353
                  new Integer(day).intValue(), new Integer(hour).intValue(),
1354
                  new Integer(minute).intValue(), new Integer(seconds).intValue());
1355
            c.set(Calendar.MILLISECOND, new Integer(milliseconds).intValue());
1356
            d = new Date(c.getTimeInMillis());
1357
            //System.out.println("d: " + d);
1358
            return d;
1359
        }
1360
        else
1361
        {  //if it's not in the expected format, try the formatter
1362
            return DateFormat.getDateTimeInstance().parse(s);
1363
        }
1364
    }
1365 5298 jones
1366 5331 jones
    /*
1367
     * Look up the information on the session using the token provided in
1368
     * the AuthToken.  The Session should have all relevant user information.
1369
     * If the session has expired or is invalid, the 'public' session will
1370
     * be returned, giving the user anonymous access.
1371
     */
1372 5394 berkley
    public static SessionData getSessionData(AuthToken token) {
1373 5331 jones
        SessionData sessionData = null;
1374
        String sessionId = "PUBLIC";
1375
        if (token != null) {
1376
            sessionId = token.getToken();
1377
        }
1378
1379
        // if the session id is registered in SessionService, get the
1380
        // SessionData for it. Otherwise, use the public session.
1381 5411 berkley
        //System.out.println("sessionid: " + sessionId);
1382 5403 berkley
        if (sessionId != null &&
1383 5406 berkley
            !sessionId.toLowerCase().equals("public") &&
1384 5403 berkley
            SessionService.getInstance().isSessionRegistered(sessionId))
1385
        {
1386 5374 berkley
            sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
1387 5331 jones
        } else {
1388 5374 berkley
            sessionData = SessionService.getInstance().getPublicSession();
1389 5331 jones
        }
1390 5411 berkley
1391 5331 jones
        return sessionData;
1392
    }
1393
1394
    /**
1395
     * Determine if a given object should be treated as an XML science metadata
1396
     * object.
1397
     *
1398
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
1399
     *
1400
     * @param sysmeta the SystemMetadata describig the object
1401
     * @return true if the object should be treated as science metadata
1402
     */
1403
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
1404 5523 berkley
        /*boolean scimeta = false;
1405 5466 berkley
        //TODO: this should be read from a .properties file instead of being hard coded
1406 5331 jones
        switch (sysmeta.getObjectFormat()) {
1407
            case EML_2_1_0: scimeta = true; break;
1408
            case EML_2_0_1: scimeta = true; break;
1409
            case EML_2_0_0: scimeta = true; break;
1410
            case FGDC_STD_001_1_1999: scimeta = true; break;
1411
            case FGDC_STD_001_1998: scimeta = true; break;
1412
            case NCML_2_2: scimeta = true; break;
1413 5521 berkley
            case DSPACE_METS_SIP_1_0: scimeta = true; break;
1414 5331 jones
        }
1415
1416 5523 berkley
        return scimeta;*/
1417
1418
        return MetadataTypeRegister.isMetadataType(sysmeta.getObjectFormat());
1419 5331 jones
    }
1420
1421 5359 berkley
    /**
1422
     * insert a data doc
1423
     * @param object
1424
     * @param guid
1425
     * @param sessionData
1426
     * @throws ServiceFailure
1427 5459 berkley
     * @returns localId of the data object inserted
1428 5359 berkley
     */
1429 5459 berkley
    private String insertDataObject(InputStream object, Identifier guid,
1430 5331 jones
            SessionData sessionData) throws ServiceFailure {
1431
1432 5431 berkley
        String username = "public";
1433
        String[] groups = null;
1434
        if(sessionData != null)
1435
        {
1436
          username = sessionData.getUserName();
1437
          groups = sessionData.getGroupNames();
1438
        }
1439 5331 jones
1440
        // generate guid/localId pair for object
1441
        logMetacat.debug("Generating a guid/localId mapping");
1442
        IdentifierManager im = IdentifierManager.getInstance();
1443
        String localId = im.generateLocalId(guid.getValue(), 1);
1444
1445
        try {
1446
            logMetacat.debug("Case DATA: starting to write to disk.");
1447
            if (DocumentImpl.getDataFileLockGrant(localId)) {
1448
1449
                // Save the data file to disk using "localId" as the name
1450
                try {
1451
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
1452
1453
                    File dataDirectory = new File(datafilepath);
1454
                    dataDirectory.mkdirs();
1455
1456
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
1457
1458
                    // TODO: Check that the file size matches SystemMetadata
1459
                    //                        long size = newFile.length();
1460
                    //                        if (size == 0) {
1461
                    //                            throw new IOException("Uploaded file is 0 bytes!");
1462
                    //                        }
1463
1464
                    // Register the file in the database (which generates an exception
1465
                    // if the localId is not acceptable or other untoward things happen
1466
                    try {
1467
                        logMetacat.debug("Registering document...");
1468
                        DocumentImpl.registerDocument(localId, "BIN", localId,
1469
                                username, groups);
1470
                        logMetacat.debug("Registration step completed.");
1471
                    } catch (SQLException e) {
1472
                        //newFile.delete();
1473
                        logMetacat.debug("SQLE: " + e.getMessage());
1474
                        e.printStackTrace(System.out);
1475 5356 berkley
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1476 5331 jones
                    } catch (AccessionNumberException e) {
1477
                        //newFile.delete();
1478
                        logMetacat.debug("ANE: " + e.getMessage());
1479
                        e.printStackTrace(System.out);
1480 5356 berkley
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1481 5331 jones
                    } catch (Exception e) {
1482
                        //newFile.delete();
1483
                        logMetacat.debug("Exception: " + e.getMessage());
1484
                        e.printStackTrace(System.out);
1485 5356 berkley
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1486 5331 jones
                    }
1487
1488
                    logMetacat.debug("Logging the creation event.");
1489 5344 berkley
                    EventLog.getInstance().log(metacatUrl,
1490 5384 berkley
                            username, localId, "create");
1491 5331 jones
1492
                    // Schedule replication for this data file
1493
                    logMetacat.debug("Scheduling replication.");
1494
                    ForceReplicationHandler frh = new ForceReplicationHandler(
1495 5384 berkley
                            localId, "create", false, null);
1496 5331 jones
1497
                } catch (PropertyNotFoundException e) {
1498 5356 berkley
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
1499 5331 jones
                }
1500
            }
1501 5459 berkley
            return localId;
1502 5331 jones
        } catch (Exception e) {
1503
            // Could not get a lock on the document, so we can not update the file now
1504 5356 berkley
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
1505 5331 jones
        }
1506
    }
1507
1508 5359 berkley
    /**
1509
     * write a file to a stream
1510
     * @param dir
1511
     * @param fileName
1512
     * @param data
1513
     * @return
1514
     * @throws ServiceFailure
1515
     */
1516 5319 jones
    private File writeStreamToFile(File dir, String fileName, InputStream data)
1517
        throws ServiceFailure {
1518
1519
        File newFile = new File(dir, fileName);
1520
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
1521
1522
        try {
1523
            if (newFile.createNewFile()) {
1524
                // write data stream to desired file
1525
                OutputStream os = new FileOutputStream(newFile);
1526
                long length = IOUtils.copyLarge(data, os);
1527
                os.flush();
1528
                os.close();
1529
            } else {
1530
                logMetacat.debug("File creation failed, or file already exists.");
1531 5356 berkley
                throw new ServiceFailure("1190", "File already exists: " + fileName);
1532 5319 jones
            }
1533
        } catch (FileNotFoundException e) {
1534
            logMetacat.debug("FNF: " + e.getMessage());
1535 5356 berkley
            throw new ServiceFailure("1190", "File not found: " + fileName + " "
1536 5319 jones
                    + e.getMessage());
1537
        } catch (IOException e) {
1538
            logMetacat.debug("IOE: " + e.getMessage());
1539 5356 berkley
            throw new ServiceFailure("1190", "File was not written: " + fileName
1540 5319 jones
                    + " " + e.getMessage());
1541
        }
1542
1543
        return newFile;
1544 5329 jones
    }
1545
1546 5350 berkley
    /**
1547 5441 berkley
     * insert a systemMetadata doc, return the localId of the sysmeta
1548 5350 berkley
     */
1549 5441 berkley
    private String insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData)
1550 5377 berkley
        throws ServiceFailure
1551
    {
1552 5329 jones
        logMetacat.debug("Starting to insert SystemMetadata...");
1553
1554
        // generate guid/localId pair for sysmeta
1555 5331 jones
        Identifier sysMetaGuid = new Identifier();
1556
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
1557 5329 jones
        sysmeta.setDateSysMetadataModified(new Date());
1558 5453 berkley
        System.out.println("****inserting new system metadata with modified date " +
1559
                sysmeta.getDateSysMetadataModified());
1560 5329 jones
1561
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
1562 5444 berkley
        System.out.println("sysmeta: " + xml);
1563 5453 berkley
        String localId = insertDocument(xml, sysMetaGuid, sessionData, true);
1564 5420 berkley
        System.out.println("sysmeta inserted with localId " + localId);
1565 5453 berkley
        //insert the system metadata doc id into the systemmetadata table to
1566 5333 berkley
        //link it to the data or metadata document
1567 5377 berkley
        IdentifierManager.getInstance().createSystemMetadataMapping(
1568
                sysmeta.getIdentifier().getValue(), sysMetaGuid.getValue());
1569 5441 berkley
        return localId;
1570 5329 jones
    }
1571
1572 5333 berkley
    /**
1573 5350 berkley
     * update a systemMetadata doc
1574
     */
1575
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
1576
      throws ServiceFailure
1577
    {
1578
        try
1579
        {
1580 5424 berkley
            String smId = IdentifierManager.getInstance().getSystemMetadataLocalId(sm.getIdentifier().getValue());
1581 5630 berkley
            System.out.println("setting date modified to " + new Date());
1582 5350 berkley
            sm.setDateSysMetadataModified(new Date());
1583
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
1584 5456 berkley
            String localId = updateDocument(xml, sm.getIdentifier(), null, sessionData, true);
1585 5350 berkley
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
1586
        }
1587
        catch(Exception e)
1588
        {
1589 5356 berkley
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getMessage());
1590 5350 berkley
        }
1591
    }
1592
1593 5453 berkley
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
1594
        throws ServiceFailure
1595
    {
1596
        return insertDocument(xml, guid, sessionData, false);
1597
    }
1598
1599 5350 berkley
    /**
1600
     * insert a document
1601
     * NOTE: this method shouldn't be used from the update or create() methods.
1602
     * we shouldn't be putting the science metadata or data objects into memory.
1603
     */
1604 5453 berkley
    private String insertDocument(String xml, Identifier guid, SessionData sessionData,
1605
            boolean isSystemMetadata)
1606 5350 berkley
        throws ServiceFailure
1607
    {
1608 5453 berkley
        return insertOrUpdateDocument(xml, guid, sessionData, "insert", isSystemMetadata);
1609 5350 berkley
    }
1610
1611
    /**
1612
     * insert a document from a stream
1613
     */
1614
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
1615
      throws IOException, ServiceFailure
1616
    {
1617
        //HACK: change this eventually.  we should not be converting the stream to a string
1618
        String xml = IOUtils.toString(is);
1619
        return insertDocument(xml, guid, sessionData);
1620
    }
1621
1622
    /**
1623
     * update a document
1624
     * NOTE: this method shouldn't be used from the update or create() methods.
1625
     * we shouldn't be putting the science metadata or data objects into memory.
1626
     */
1627 5453 berkley
    private String updateDocument(String xml, Identifier obsoleteGuid,
1628
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1629 5350 berkley
        throws ServiceFailure
1630
    {
1631 5453 berkley
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update", isSystemMetadata);
1632 5350 berkley
    }
1633
1634
    /**
1635
     * update a document from a stream
1636
     */
1637 5453 berkley
    private String updateDocument(InputStream is, Identifier obsoleteGuid,
1638
            Identifier guid, SessionData sessionData, boolean isSystemMetadata)
1639 5350 berkley
      throws IOException, ServiceFailure
1640
    {
1641
        //HACK: change this eventually.  we should not be converting the stream to a string
1642
        String xml = IOUtils.toString(is);
1643 5453 berkley
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData, isSystemMetadata);
1644 5350 berkley
        IdentifierManager im = IdentifierManager.getInstance();
1645
        if(guid != null)
1646
        {
1647
          im.createMapping(guid.getValue(), localId);
1648
        }
1649
        return localId;
1650
    }
1651
1652
    /**
1653 5333 berkley
     * insert a document, return the id of the document that was inserted
1654
     */
1655 5453 berkley
    protected String insertOrUpdateDocument(String xml, Identifier guid,
1656
            SessionData sessionData, String insertOrUpdate, boolean isSystemMetadata)
1657 5329 jones
        throws ServiceFailure {
1658
        logMetacat.debug("Starting to insert xml document...");
1659
        IdentifierManager im = IdentifierManager.getInstance();
1660
1661
        // generate guid/localId pair for sysmeta
1662 5350 berkley
        String localId = null;
1663
        if(insertOrUpdate.equals("insert"))
1664
        {
1665 5453 berkley
            localId = im.generateLocalId(guid.getValue(), 1, isSystemMetadata);
1666 5350 berkley
        }
1667
        else
1668
        {
1669
            //localid should already exist in the identifier table, so just find it
1670
            try
1671
            {
1672 5456 berkley
                System.out.println("updating guid " + guid.getValue());
1673
                if(!isSystemMetadata)
1674
                {
1675
                    System.out.println("looking in identifier table for guid " + guid.getValue());
1676
                    localId = im.getLocalId(guid.getValue());
1677
                }
1678
                else
1679
                {
1680
                    System.out.println("looking in systemmetadata table for guid " + guid.getValue());
1681
                    localId = im.getSystemMetadataLocalId(guid.getValue());
1682
                }
1683
                System.out.println("localId: " + localId);
1684 5350 berkley
                //increment the revision
1685
                String docid = localId.substring(0, localId.lastIndexOf("."));
1686
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
1687
                int rev = new Integer(revS).intValue();
1688
                rev++;
1689
                docid = docid + "." + rev;
1690
                localId = docid;
1691 5456 berkley
                System.out.println("incremented localId: " + localId);
1692 5350 berkley
            }
1693
            catch(McdbDocNotFoundException e)
1694
            {
1695 5356 berkley
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
1696 5350 berkley
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
1697
            }
1698
        }
1699 5331 jones
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
1700 5329 jones
                localId);
1701
1702
        String[] action = new String[1];
1703 5350 berkley
        action[0] = insertOrUpdate;
1704 5329 jones
        params.put("action", action);
1705
        String[] docid = new String[1];
1706
        docid[0] = localId;
1707
        params.put("docid", docid);
1708
        String[] doctext = new String[1];
1709
        doctext[0] = xml;
1710
        logMetacat.debug(doctext[0]);
1711
        params.put("doctext", doctext);
1712 5331 jones
1713
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
1714
        // onto output stream, or alternatively, capture that and parse it to
1715
        // generate the right exceptions
1716 5337 berkley
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
1717
        //PrintWriter pw = new PrintWriter(output);
1718 5449 berkley
        String username = "public";
1719
        String[] groupnames = null;
1720
        if(sessionData != null)
1721
        {
1722
            username = sessionData.getUserName();
1723
            groupnames = sessionData.getGroupNames();
1724
        }
1725 5344 berkley
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null,
1726 5449 berkley
                            null, params, username, groupnames);
1727 5461 berkley
        if(result.indexOf("<error>") != -1)
1728
        {
1729
            throw new ServiceFailure("1000", "Error inserting or updating document: " + result);
1730
        }
1731 5337 berkley
        //String outputS = new String(output.toByteArray());
1732
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
1733 5333 berkley
        logMetacat.debug("Finsished inserting xml document with id " + localId);
1734
        return localId;
1735 5329 jones
    }
1736
1737 5359 berkley
    /**
1738 5555 berkley
     * serialize a dataone type
1739 5550 berkley
     */
1740 5555 berkley
    private void serializeServiceType(Class type, Object object, OutputStream out)
1741
        throws JiBXException
1742 5550 berkley
    {
1743 5555 berkley
        IBindingFactory bfact = BindingDirectory.getFactory(type);
1744
        IMarshallingContext mctx = bfact.createMarshallingContext();
1745
        mctx.marshalDocument(object, "UTF-8", null, out);
1746 5550 berkley
    }
1747
1748
    /**
1749 5359 berkley
     * serialize a system metadata doc
1750
     * @param sysmeta
1751
     * @return
1752
     * @throws ServiceFailure
1753
     */
1754 5332 jones
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta)
1755 5329 jones
        throws ServiceFailure {
1756
        IBindingFactory bfact;
1757
        ByteArrayOutputStream sysmetaOut = null;
1758
        try {
1759
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
1760
            IMarshallingContext mctx = bfact.createMarshallingContext();
1761
            sysmetaOut = new ByteArrayOutputStream();
1762
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
1763
        } catch (JiBXException e) {
1764 5382 berkley
            e.printStackTrace();
1765 5356 berkley
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
1766 5329 jones
        }
1767
1768
        return sysmetaOut;
1769
    }
1770 5332 jones
1771 5359 berkley
    /**
1772
     * deserialize a system metadata doc
1773
     * @param xml
1774
     * @return
1775
     * @throws ServiceFailure
1776
     */
1777 5332 jones
    public static SystemMetadata deserializeSystemMetadata(InputStream xml)
1778
        throws ServiceFailure {
1779
        try {
1780
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
1781
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1782
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
1783
            return sysmeta;
1784
        } catch (JiBXException e) {
1785 5382 berkley
            e.printStackTrace();
1786
            throw new ServiceFailure("1190", "Failed to deserialize and insert SystemMetadata: " + e.getMessage());
1787 5332 jones
        }
1788
    }
1789 5378 berkley
1790
    /**
1791 5683 berkley
     * read a document from metacat and return the InputStream
1792
     *
1793
     * @param localId
1794
     * @param username
1795
     * @param groups
1796
     * @return
1797
     * @throws InsufficientKarmaException
1798
     * @throws ParseLSIDException
1799
     * @throws PropertyNotFoundException
1800
     * @throws McdbException
1801
     * @throws SQLException
1802
     * @throws ClassNotFoundException
1803
     * @throws IOException
1804
     */
1805
    private InputStream readFromMetacat(String localId, String username, String[] groups)
1806
        throws InsufficientKarmaException, ParseLSIDException,
1807
        PropertyNotFoundException, McdbException, SQLException,
1808
        ClassNotFoundException, IOException
1809
    {
1810
        File tmpDir;
1811
        try
1812
        {
1813
            tmpDir = new File(PropertyService.getProperty("application.tempDir"));
1814
        }
1815
        catch(PropertyNotFoundException pnfe)
1816
        {
1817
            logMetacat.error("ResourceHandler.writeMMPPartstoFiles: " +
1818
                    "application.tmpDir not found.  Using /tmp instead.");
1819
            tmpDir = new File("/tmp");
1820
        }
1821
        Date d = new Date();
1822
        final File outputFile = new File(tmpDir, "metacat.output." + d.getTime());
1823
        FileOutputStream dataSink = new FileOutputStream(outputFile);
1824
1825
        handler.readFromMetacat(metacatUrl, null,
1826
                dataSink, localId, "xml",
1827
                username,
1828
                groups, true, params);
1829
1830
        //set a timer to clean up the temp files
1831
        Timer t = new Timer();
1832
        TimerTask tt = new TimerTask() {
1833
            @Override
1834
            public void run()
1835
            {
1836
                outputFile.delete();
1837
            }
1838
        };
1839
        t.schedule(tt, 20000); //schedule after 20 secs
1840
1841
        InputStream objectStream = new FileInputStream(outputFile);
1842
        return objectStream;
1843
    }
1844
1845
    /**
1846 5621 berkley
     * return an MD5 checksum for the stream
1847
     * @param is
1848
     * @return
1849 5378 berkley
     */
1850
    private String checksum(InputStream is)
1851 5621 berkley
        throws Exception
1852
    {
1853
        return checksum(is, "MD5");
1854
    }
1855
1856
    /**
1857
     * produce a checksum for item using the given algorithm
1858
     */
1859
    private String checksum(InputStream is, String algorithm)
1860 5378 berkley
      throws Exception
1861
    {
1862
        byte[] buffer = new byte[1024];
1863 5621 berkley
        MessageDigest complete = MessageDigest.getInstance(algorithm);
1864 5378 berkley
        int numRead;
1865
1866
        do
1867
        {
1868
          numRead = is.read(buffer);
1869
          if (numRead > 0)
1870
          {
1871
            complete.update(buffer, 0, numRead);
1872
          }
1873
        } while (numRead != -1);
1874
1875
1876
        return getHex(complete.digest());
1877
    }
1878
1879
    /**
1880
     * convert a byte array to a hex string
1881
     */
1882
    private static String getHex( byte [] raw )
1883
    {
1884
        final String HEXES = "0123456789ABCDEF";
1885
        if ( raw == null ) {
1886
          return null;
1887
        }
1888
        final StringBuilder hex = new StringBuilder( 2 * raw.length );
1889
        for ( final byte b : raw ) {
1890
          hex.append(HEXES.charAt((b & 0xF0) >> 4))
1891
             .append(HEXES.charAt((b & 0x0F)));
1892
        }
1893
        return hex.toString();
1894
    }
1895
1896
    /**
1897
     * parse the metacat date which looks like 2010-06-08 (YYYY-MM-DD) into
1898
     * a proper date object
1899
     * @param date
1900
     * @return
1901
     */
1902
    private Date parseMetacatDate(String date)
1903
    {
1904
        String year = date.substring(0, 4);
1905
        String month = date.substring(5, 7);
1906
        String day = date.substring(8, 10);
1907 5420 berkley
        Calendar c = Calendar.getInstance(TimeZone.getDefault());
1908 5378 berkley
        c.set(new Integer(year).intValue(),
1909
              new Integer(month).intValue(),
1910
              new Integer(day).intValue());
1911 5420 berkley
        System.out.println("time in parseMetacatDate: " + c.getTime());
1912 5378 berkley
        return c.getTime();
1913
    }
1914
1915
    /**
1916
     * find the size (in bytes) of a stream
1917
     * @param is
1918
     * @return
1919
     * @throws IOException
1920
     */
1921
    private long sizeOfStream(InputStream is)
1922
        throws IOException
1923
    {
1924
        long size = 0;
1925
        byte[] b = new byte[1024];
1926
        int numread = is.read(b, 0, 1024);
1927
        while(numread != -1)
1928
        {
1929
            size += numread;
1930
            numread = is.read(b, 0, 1024);
1931
        }
1932
        return size;
1933
    }
1934 5379 berkley
1935
    /**
1936
     * create system metadata with a specified id, doc and format
1937
     */
1938
    private SystemMetadata createSystemMetadata(String localId, AuthToken token)
1939
      throws Exception
1940
    {
1941
        IdentifierManager im = IdentifierManager.getInstance();
1942 5441 berkley
        Hashtable<String, Object> docInfo = im.getDocumentInfo(localId);
1943 5379 berkley
1944
        //get the document text
1945
        int rev = im.getLatestRevForLocalId(localId);
1946
        Identifier identifier = new Identifier();
1947 5798 berkley
        try
1948
        {
1949
            identifier.setValue(im.getGUID(localId, rev));
1950
        }
1951
        catch(McdbDocNotFoundException mcdbe)
1952
        { //we're creating a new SM doc for a doc that is not in the identifier table
1953
          //so we need to add it to
1954
            System.out.println("No guid in the identifier table.  adding it for " + localId);
1955
            im.createMapping(localId, localId);
1956
            System.out.println("mapping created for " + localId);
1957
            AccessionNumber accNum = new AccessionNumber(localId, "NONE");
1958
            identifier.setValue(im.getGUID(accNum.getDocid(), rev));
1959
        }
1960
1961
        System.out.println("creating system metadata for guid " + identifier.getValue());
1962 5379 berkley
        InputStream is = this.get(token, identifier);
1963
1964
        SystemMetadata sm = new SystemMetadata();
1965
        //set the id
1966
        sm.setIdentifier(identifier);
1967
1968
        //set the object format
1969 5441 berkley
        String doctype = (String)docInfo.get("doctype");
1970
        ObjectFormat format = ObjectFormat.convert((String)docInfo.get("doctype"));
1971 5379 berkley
        if(format == null)
1972
        {
1973
            if(doctype.trim().equals("BIN"))
1974
            {
1975 5521 berkley
                format = ObjectFormat.OCTET_STREAM;
1976 5379 berkley
            }
1977
            else
1978
            {
1979
                format = ObjectFormat.convert("text/plain");
1980
            }
1981
        }
1982
        sm.setObjectFormat(format);
1983
1984
        //create the checksum
1985
        String checksumS = checksum(is);
1986
        ChecksumAlgorithm ca = ChecksumAlgorithm.convert("MD5");
1987
        Checksum checksum = new Checksum();
1988
        checksum.setValue(checksumS);
1989
        checksum.setAlgorithm(ca);
1990
        sm.setChecksum(checksum);
1991
1992
        //set the size
1993
        is = this.get(token, identifier);
1994
        sm.setSize(sizeOfStream(is));
1995
1996
        //submitter
1997
        Principal p = new Principal();
1998 5441 berkley
        p.setValue((String)docInfo.get("user_owner"));
1999 5379 berkley
        sm.setSubmitter(p);
2000
        sm.setRightsHolder(p);
2001
        try
2002
        {
2003 5441 berkley
            Date dateCreated = parseMetacatDate((String)docInfo.get("date_created"));
2004 5379 berkley
            sm.setDateUploaded(dateCreated);
2005 5441 berkley
            Date dateUpdated = parseMetacatDate((String)docInfo.get("date_updated"));
2006 5379 berkley
            sm.setDateSysMetadataModified(dateUpdated);
2007
        }
2008
        catch(Exception e)
2009
        {
2010 5428 berkley
            System.out.println("POSSIBLE ERROR: couldn't parse a date: " + e.getMessage());
2011 5379 berkley
            Date dateCreated = new Date();
2012
            sm.setDateUploaded(dateCreated);
2013
            Date dateUpdated = new Date();
2014
            sm.setDateSysMetadataModified(dateUpdated);
2015
        }
2016
        NodeReference nr = new NodeReference();
2017 5466 berkley
        //TODO: this should be set to be something more meaningful once the registry is up
2018 5379 berkley
        nr.setValue("metacat");
2019
        sm.setOriginMemberNode(nr);
2020
        sm.setAuthoritativeMemberNode(nr);
2021
        return sm;
2022
    }
2023 5298 jones
}