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