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