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
import java.io.FileNotFoundException;
28
import java.io.FileOutputStream;
29 5298 jones
import java.io.IOException;
30
import java.io.InputStream;
31
import java.io.OutputStream;
32 5329 jones
import java.io.PrintWriter;
33 5378 berkley
import java.io.StringBufferInputStream;
34
import java.security.MessageDigest;
35 5298 jones
import java.sql.SQLException;
36 5366 berkley
import java.util.*;
37 5364 berkley
import java.text.DateFormat;
38 5420 berkley
import java.text.SimpleDateFormat;
39 5298 jones
40
import javax.servlet.ServletContext;
41
import javax.servlet.http.HttpServletRequest;
42
import javax.servlet.http.HttpServletResponse;
43
44 5319 jones
import org.apache.commons.io.IOUtils;
45
import org.apache.log4j.Logger;
46 5298 jones
import org.dataone.service.exceptions.IdentifierNotUnique;
47
import org.dataone.service.exceptions.InsufficientResources;
48
import org.dataone.service.exceptions.InvalidRequest;
49
import org.dataone.service.exceptions.InvalidSystemMetadata;
50
import org.dataone.service.exceptions.InvalidToken;
51
import org.dataone.service.exceptions.NotAuthorized;
52
import org.dataone.service.exceptions.NotFound;
53
import org.dataone.service.exceptions.NotImplemented;
54
import org.dataone.service.exceptions.ServiceFailure;
55
import org.dataone.service.exceptions.UnsupportedType;
56
import org.dataone.service.mn.MemberNodeCrud;
57 5364 berkley
import org.dataone.service.types.*;
58 5329 jones
import org.jibx.runtime.BindingDirectory;
59
import org.jibx.runtime.IBindingFactory;
60
import org.jibx.runtime.IMarshallingContext;
61 5332 jones
import org.jibx.runtime.IUnmarshallingContext;
62 5329 jones
import org.jibx.runtime.JiBXException;
63 5298 jones
64
import com.gc.iotools.stream.is.InputStreamFromOutputStream;
65
66 5319 jones
import edu.ucsb.nceas.metacat.AccessionNumberException;
67 5363 berkley
import edu.ucsb.nceas.metacat.MetacatResultSet;
68
import edu.ucsb.nceas.metacat.MetacatResultSet.Document;
69 5319 jones
import edu.ucsb.nceas.metacat.DocumentImpl;
70
import edu.ucsb.nceas.metacat.EventLog;
71 5298 jones
import edu.ucsb.nceas.metacat.IdentifierManager;
72
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
73
import edu.ucsb.nceas.metacat.McdbException;
74
import edu.ucsb.nceas.metacat.MetacatHandler;
75
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
76 5370 berkley
import edu.ucsb.nceas.metacat.client.rest.MetacatRestClient;
77 5319 jones
import edu.ucsb.nceas.metacat.properties.PropertyService;
78
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
79 5298 jones
import edu.ucsb.nceas.metacat.service.SessionService;
80 5329 jones
import edu.ucsb.nceas.metacat.util.DocumentUtil;
81 5298 jones
import edu.ucsb.nceas.metacat.util.SessionData;
82
import edu.ucsb.nceas.utilities.ParseLSIDException;
83
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
84
85
/**
86
 *
87
 * Implements DataONE MemberNode CRUD API for Metacat.
88
 *
89
 * @author Matthew Jones
90
 */
91 5394 berkley
public class CrudService implements MemberNodeCrud
92
{
93 5337 berkley
    private static CrudService crudService = null;
94 5298 jones
95
    private MetacatHandler handler;
96
    private Hashtable<String, String[]> params;
97 5392 berkley
    private Logger logMetacat = null;
98
    private Logger logCrud = null;
99 5337 berkley
100
    private String metacatUrl;
101 5420 berkley
102
    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
103 5298 jones
104
    /**
105 5337 berkley
     * singleton accessor
106
     */
107
    public static CrudService getInstance()
108
    {
109
      if(crudService == null)
110
      {
111
        crudService = new CrudService();
112
      }
113
114
      return crudService;
115
    }
116
117
    /**
118 5298 jones
     * Initializes new instance by setting servlet context,request and response.
119
     */
120 5337 berkley
    public CrudService() {
121 5333 berkley
    //change crud service into a singleton.  dont pass servlet data structures here
122 5337 berkley
        logMetacat = Logger.getLogger(CrudService.class);
123 5392 berkley
        logCrud = Logger.getLogger("DataOneLogger");
124 5337 berkley
        try
125
        {
126
            String server = PropertyService.getProperty("server.name");
127
            String port = PropertyService.getProperty("server.httpPort");
128
            String context = PropertyService.getProperty("application.context");
129
            metacatUrl = "http://" + server + ":" + port + "/" + context;
130
            logMetacat.debug("Initializing CrudService with url " + metacatUrl);
131
        }
132
        catch(Exception e)
133
        {
134
            logMetacat.error("Could not find servlet url in CrudService: " + e.getMessage());
135
            e.printStackTrace();
136
            throw new RuntimeException("Error getting servlet url in CrudService: " + e.getMessage());
137
        }
138
139
        /*this.servletContext = servletContext;
140 5298 jones
        this.request = request;
141 5337 berkley
        this.response = response;*/
142
143
        params = new Hashtable<String, String[]>();
144 5298 jones
145 5337 berkley
        handler = new MetacatHandler(new Timer());
146
147 5298 jones
    }
148 5337 berkley
149 5298 jones
    /**
150 5337 berkley
     * return the context url CrudService is using.
151 5298 jones
     */
152 5337 berkley
    public String getContextUrl()
153
    {
154
        return metacatUrl;
155
    }
156
157
    /**
158 5394 berkley
     * Set the context url that this service uses.  It is normally not necessary
159
     * to call this method unless you are trying to connect to a server other
160
     * than the one in which this service is installed.  Otherwise, this value is
161
     * taken from the metacat.properties file (server.name, server.port, application.context).
162
     */
163
    public void setContextUrl(String url)
164
    {
165
        metacatUrl = url;
166
    }
167
168
    /**
169 5337 berkley
     * set the params for this service from an HttpServletRequest param list
170
     */
171
    public void setParamsFromRequest(HttpServletRequest request)
172
    {
173 5298 jones
        Enumeration paramlist = request.getParameterNames();
174
        while (paramlist.hasMoreElements()) {
175 5337 berkley
            String name = (String) paramlist.nextElement();
176
            String[] value = (String[])request.getParameterValues(name);
177 5298 jones
            params.put(name, value);
178
        }
179
    }
180
181 5337 berkley
    /**
182 5370 berkley
     * Authenticate against metacat and get a token.
183
     * @param username
184
     * @param password
185
     * @return
186
     * @throws ServiceFailure
187
     */
188
    public AuthToken authenticate(String username, String password)
189
      throws ServiceFailure
190
    {
191
        try
192
        {
193
            MetacatRestClient restClient = new MetacatRestClient(getContextUrl());
194
            String response = restClient.login(username, password);
195
            String sessionid = restClient.getSessionId();
196
            SessionService sessionService = SessionService.getInstance();
197
            sessionService.registerSession(new SessionData(sessionid, username, new String[0], password, "CrudServiceLogin"));
198
            AuthToken token = new AuthToken(sessionid);
199 5383 berkley
            EventLog.getInstance().log(metacatUrl,
200 5384 berkley
                    username, null, "authenticate");
201 5392 berkley
            logCrud.info("authenticate");
202 5370 berkley
            return token;
203
        }
204
        catch(Exception e)
205
        {
206
            throw new ServiceFailure("1000", "Error authenticating with metacat: " + e.getMessage());
207
        }
208
    }
209
210
    /**
211 5337 berkley
     * set the parameter values needed for this request
212
     */
213
    public void setParameter(String name, String[] value)
214
    {
215
        params.put(name, value);
216
    }
217
218 5377 berkley
    /**
219
     * Generate SystemMetadata for any object in the object store that does
220
     * not already have it.  SystemMetadata documents themselves, are, of course,
221
     * exempt.  This is a utility method for migration of existing object
222
     * stores to DataONE where SystemMetadata is required for all objects.  See
223
     * https://trac.dataone.org/ticket/591
224 5378 berkley
     *
225
     * @param token an authtoken with appropriate permissions to read all
226
     * documents in the object store.  To work correctly, this should probably
227
     * be an adminstrative credential.
228 5377 berkley
     */
229 5378 berkley
    public void generateMissingSystemMetadata(AuthToken token)
230 5377 berkley
    {
231 5378 berkley
        IdentifierManager im = IdentifierManager.getInstance();
232 5377 berkley
        //get the list of ids with no SM
233 5378 berkley
        List<String> l = im.getLocalIdsWithNoSystemMetadata();
234 5377 berkley
        for(int i=0; i<l.size(); i++)
235
        { //for each id, add a system metadata doc
236 5378 berkley
            String localId = l.get(i);
237 5387 berkley
            //System.out.println("Creating SystemMetadata for localId " + localId);
238 5378 berkley
            //get the document
239
            try
240
            {
241
                //generate required system metadata fields from the document
242
                SystemMetadata sm = createSystemMetadata(localId, token);
243
                //insert the systemmetadata object
244 5379 berkley
                SessionData sessionData = getSessionData(token);
245
                insertSystemMetadata(sm, sessionData);
246 5383 berkley
247
                String username = sessionData.getUserName();
248
                EventLog.getInstance().log(metacatUrl,
249 5384 berkley
                        username, localId, "generateMissingSystemMetadata");
250 5378 berkley
            }
251
            catch(Exception e)
252
            {
253 5379 berkley
                //e.printStackTrace();
254 5378 berkley
                System.out.println("Exception generating missing system metadata: " + e.getMessage());
255
                logMetacat.error("Could not generate missing system metadata: " + e.getMessage());
256
            }
257 5377 berkley
        }
258 5392 berkley
        logCrud.info("generateMissingSystemMetadata");
259 5377 berkley
    }
260
261 5378 berkley
    /**
262 5379 berkley
     * create an object via the crud interface
263 5378 berkley
     */
264 5320 jones
    public Identifier create(AuthToken token, Identifier guid,
265 5298 jones
            InputStream object, SystemMetadata sysmeta) throws InvalidToken,
266
            ServiceFailure, NotAuthorized, IdentifierNotUnique, UnsupportedType,
267
            InsufficientResources, InvalidSystemMetadata, NotImplemented {
268 5319 jones
269
        logMetacat.debug("Starting CrudService.create()...");
270
271 5329 jones
        // authenticate & get user info
272
        SessionData sessionData = getSessionData(token);
273
        String username = sessionData.getUserName();
274
        String[] groups = sessionData.getGroupNames();
275 5383 berkley
        String localId = null;
276 5319 jones
277 5376 berkley
        if (username == null || username.equals("public"))
278
        {
279
            throw new NotAuthorized("1000", "User " + username + " is not authorized to create content." +
280
                    "  If you are not logged in, please do so and retry the request.");
281
        }
282
283 5319 jones
        // verify that guid == SystemMetadata.getIdentifier()
284 5320 jones
        logMetacat.debug("Comparing guid|sysmeta_guid: " + guid.getValue() + "|" + sysmeta.getIdentifier().getValue());
285 5329 jones
        if (!guid.getValue().equals(sysmeta.getIdentifier().getValue())) {
286 5356 berkley
            throw new InvalidSystemMetadata("1180",
287 5329 jones
                "GUID in method call does not match GUID in system metadata.");
288
        }
289 5319 jones
290
        logMetacat.debug("Checking if identifier exists...");
291
        // Check that the identifier does not already exist
292
        IdentifierManager im = IdentifierManager.getInstance();
293 5320 jones
        if (im.identifierExists(guid.getValue())) {
294 5356 berkley
            throw new IdentifierNotUnique("1120",
295 5319 jones
                "GUID is already in use by an existing object.");
296
        }
297
298 5329 jones
        // Check if we are handling metadata or data
299
        boolean isScienceMetadata = isScienceMetadata(sysmeta);
300 5319 jones
301 5329 jones
        if (isScienceMetadata) {
302 5331 jones
            // CASE METADATA:
303
            try {
304 5350 berkley
                this.insertDocument(object, guid, sessionData);
305 5383 berkley
                localId = im.getLocalId(guid.getValue());
306 5331 jones
            } catch (IOException e) {
307
                String msg = "Could not create string from XML stream: " +
308
                    " " + e.getMessage();
309
                logMetacat.debug(msg);
310 5356 berkley
                throw new ServiceFailure("1190", msg);
311 5383 berkley
            } catch(Exception e) {
312
                String msg = "Unexpected error in CrudService.create: " + e.getMessage();
313
                logMetacat.debug(msg);
314
                throw new ServiceFailure("1190", msg);
315 5331 jones
            }
316 5383 berkley
317 5319 jones
318 5329 jones
        } else {
319 5331 jones
            // DEFAULT CASE: DATA (needs to be checked and completed)
320
            insertDataObject(object, guid, sessionData);
321
322
        }
323 5319 jones
324 5331 jones
        // For Metadata and Data, insert the system metadata into the object store too
325
        insertSystemMetadata(sysmeta, sessionData);
326 5319 jones
        logMetacat.debug("Returning from CrudService.create()");
327 5383 berkley
        EventLog.getInstance().log(metacatUrl,
328 5384 berkley
                username, localId, "create");
329 5393 berkley
        logCrud.info("create localId:" + localId + " guid:" + guid.getValue());
330 5319 jones
        return guid;
331 5298 jones
    }
332 5353 berkley
333
    /**
334
     * update an existing object with a new object.  Change the system metadata
335
     * to reflect the changes and update it as well.
336
     */
337
    public Identifier update(AuthToken token, Identifier guid,
338
            InputStream object, Identifier obsoletedGuid, SystemMetadata sysmeta)
339
            throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique,
340
            UnsupportedType, InsufficientResources, NotFound, InvalidSystemMetadata,
341
            NotImplemented {
342
        try
343
        {
344
            SessionData sessionData = getSessionData(token);
345
346
            //find the old systemmetadata (sm.old) document id (the one linked to obsoletedGuid)
347
            SystemMetadata sm = getSystemMetadata(token, obsoletedGuid);
348
            //change sm.old's obsoletedBy field
349
            List l = sm.getObsoletedByList();
350
            l.add(guid);
351
            sm.setObsoletedByList(l);
352
            //update sm.old
353
            updateSystemMetadata(sm, sessionData);
354
355
            //change the obsoletes field of the new systemMetadata (sm.new) to point to the id of the old one
356
            sysmeta.addObsolete(obsoletedGuid);
357
            //insert sm.new
358
            insertSystemMetadata(sysmeta, sessionData);
359
360
            boolean isScienceMetadata = isScienceMetadata(sysmeta);
361
            if(isScienceMetadata)
362
            {
363
                //update the doc
364
                updateDocument(object, obsoletedGuid, guid, sessionData);
365
            }
366
            else
367
            {
368
                //update a data file, not xml
369
                insertDataObject(object, guid, sessionData);
370
            }
371 5383 berkley
372
            IdentifierManager im = IdentifierManager.getInstance();
373
            String username = sessionData.getUserName();
374
            EventLog.getInstance().log(metacatUrl,
375 5384 berkley
                    username, im.getLocalId(guid.getValue()), "update");
376 5393 berkley
            logCrud.info("update localId:" + im.getLocalId(guid.getValue()) + " guid:" + guid.getValue());
377 5353 berkley
            return guid;
378
        }
379
        catch(Exception e)
380
        {
381 5356 berkley
            throw new ServiceFailure("1030", "Error updating document in CrudService: " + e.getMessage());
382 5353 berkley
        }
383
    }
384 5359 berkley
385
    /**
386 5362 berkley
     * set the permission on the document
387
     * @param token
388
     * @param principal
389
     * @param permission
390
     * @param permissionType
391
     * @param permissionOrder
392
     * @return
393
     */
394
    public void setAccess(AuthToken token, Identifier id, String principal, String permission,
395 5424 berkley
            String permissionType, String permissionOrder, boolean setSystemMetadata)
396 5362 berkley
      throws ServiceFailure
397
    {
398
        try
399
        {
400
            IdentifierManager im = IdentifierManager.getInstance();
401
            String docid = im.getLocalId(id.getValue());
402
            final SessionData sessionData = getSessionData(token);
403
            String permNum = "0";
404 5370 berkley
            if(permission.equals("read"))
405 5362 berkley
            {
406
                permNum = "4";
407
            }
408 5370 berkley
            else if(permission.equals("write"))
409 5362 berkley
            {
410
                permNum = "6";
411
            }
412 5424 berkley
            System.out.println("user " + sessionData.getUserName() +
413
                    " is setting access level " + permNum + " for permission " +
414
                    permissionType + " on doc with localid " + docid);
415 5362 berkley
            handler.setAccess(metacatUrl, sessionData.getUserName(), docid,
416
                    principal, permNum, permissionType, permissionOrder);
417 5424 berkley
            if(setSystemMetadata)
418
            {
419
                //set the same perms on the system metadata doc
420
                String smlocalid = im.getSystemMetadataLocalId(id.getValue());
421
                System.out.println("setting access on SM doc with localid " + smlocalid);
422
                //cs.setAccess(token, smid, principal, permission, permissionType, permissionOrder);
423
                handler.setAccess(metacatUrl, sessionData.getUserName(), smlocalid,
424
                        principal, permNum, permissionType, permissionOrder);
425
            }
426 5383 berkley
            String username = sessionData.getUserName();
427
            EventLog.getInstance().log(metacatUrl,
428 5384 berkley
                    username, im.getLocalId(id.getValue()), "setAccess");
429 5392 berkley
            logCrud.info("setAccess");
430 5362 berkley
        }
431
        catch(Exception e)
432
        {
433 5370 berkley
            e.printStackTrace();
434 5362 berkley
            throw new ServiceFailure("1000", "Could not set access on the document with id " + id.getValue());
435
        }
436
    }
437
438
    /**
439 5359 berkley
     *  Retrieve the list of objects present on the MN that match the calling
440
     *  parameters. This method is required to support the process of Member
441
     *  Node synchronization. At a minimum, this method should be able to
442
     *  return a list of objects that match:
443
     *  startTime <= SystemMetadata.dateSysMetadataModified
444
     *  but is expected to also support date range (by also specifying endTime),
445
     *  and should also support slicing of the matching set of records by
446
     *  indicating the starting index of the response (where 0 is the index
447
     *  of the first item) and the count of elements to be returned.
448
     *
449 5364 berkley
     *  If startTime or endTime is null, the query is not restricted by that parameter.
450
     *
451 5359 berkley
     * @see http://mule1.dataone.org/ArchitectureDocs/mn_api_replication.html#MN_replication.listObjects
452
     * @param token
453
     * @param startTime
454
     * @param endTime
455
     * @param objectFormat
456
     * @param replicaStatus
457
     * @param start
458
     * @param count
459
     * @return ObjectList
460
     * @throws NotAuthorized
461
     * @throws InvalidRequest
462
     * @throws NotImplemented
463
     * @throws ServiceFailure
464
     * @throws InvalidToken
465
     */
466
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime,
467
        ObjectFormat objectFormat, boolean replicaStatus, int start, int count)
468
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
469
    {
470
      ObjectList ol = new ObjectList();
471 5362 berkley
      final SessionData sessionData = getSessionData(token);
472 5413 berkley
      int totalAfterQuery = 0;
473 5362 berkley
      try
474
      {
475 5427 berkley
476 5362 berkley
          params.clear();
477
          params.put("returndoctype", new String[] {"http://dataone.org/service/types/SystemMetadata/0.1"});
478
          params.put("qformat", new String[] {"xml"});
479 5364 berkley
          params.put("returnfield", new String[] {"size", "originMemberNode",
480 5387 berkley
                  "identifier", "objectFormat", "dateSysMetadataModified", "checksum", "checksum/@algorithm"});
481 5362 berkley
          params.put("anyfield", new String[] {"%"});
482 5363 berkley
483 5411 berkley
          //System.out.println("query is: metacatUrl: " + metacatUrl + " user: " + sessionData.getUserName() +
484
          //    " sessionid: " + sessionData.getId() + " params: " + params.toString());
485 5426 berkley
          String username = "public";
486
          String[] groups = null;
487
          String sessionid = "";
488
          if(sessionData != null)
489
          {
490
              username = sessionData.getUserName();
491
              groups = sessionData.getGroupNames();
492
              sessionid = sessionData.getId();
493
          }
494 5408 berkley
495 5426 berkley
          MetacatResultSet rs = handler.query(metacatUrl, params, username,
496
                  groups, sessionid);
497 5363 berkley
          List docs = rs.getDocuments();
498 5369 berkley
          if(count == 1000)
499 5363 berkley
          {
500 5369 berkley
              count = docs.size();
501
          }
502 5403 berkley
503 5413 berkley
          //System.out.println("query returned " + docs.size() + " documents.");
504
          Vector<Document> docCopy = new Vector<Document>();
505 5408 berkley
506 5413 berkley
          //preparse the list to remove any that don't match the query params
507
          for(int i=0; i<docs.size(); i++)
508 5369 berkley
          {
509 5363 berkley
              Document d = (Document)docs.get(i);
510 5364 berkley
              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
511 5413 berkley
512
              if(returnedObjectFormat != null &&
513
                 objectFormat != null &&
514
                 !objectFormat.toString().trim().equals(returnedObjectFormat.toString().trim()))
515 5364 berkley
              { //make sure the objectFormat is the one specified
516
                  continue;
517
              }
518 5384 berkley
519
              String dateSMM = d.getField("dateSysMetadataModified");
520 5413 berkley
              if((startTime != null || endTime != null) && dateSMM == null)
521
              {  //if startTime or endTime are not null, we need a date to compare to
522 5384 berkley
                  continue;
523
              }
524 5413 berkley
525
              //date parse
526
              Date dateSysMetadataModified = null;
527
              if(dateSMM != null)
528
              {
529 5420 berkley
                  //dateSysMetadataModified = parseDate(dateSMM);
530
                  if(dateSMM.indexOf(".") != -1)
531
                  {  //strip the milliseconds
532
                      dateSMM = dateSMM.substring(0, dateSMM.indexOf(".")) + 'Z';
533
                  }
534
                  //System.out.println("dateSMM: " + dateSMM);
535
                  dateFormat.setTimeZone(TimeZone.getTimeZone("GMT-0"));
536
                  dateSysMetadataModified = dateFormat.parse(dateSMM);
537
                  //System.out.println("dateSysMetadataModified: " + dateSysMetadataModified);
538
539
                  //dateSysMetadataModified = getDateInTimeZone(dateSysMetadataModified, TimeZone.getDefault().getID());
540
                  //System.out.println("dateSysMetadataModified after convertion to default timezone: " + dateSysMetadataModified);
541 5413 berkley
              }
542 5420 berkley
              /*System.out.println("docid: " + d.docid);
543
              System.out.println("dateSysMetadataModified: " + dateSysMetadataModified);
544
              System.out.println("startTime: " + startTime);
545
              System.out.println("endtime: " + endTime);*/
546 5427 berkley
547 5364 berkley
              int startDateComparison = 0;
548
              int endDateComparison = 0;
549
              if(startTime != null)
550
              {
551 5427 berkley
                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
552
                  zTime.setTime(startTime);
553
                  startTime = zTime.getTime();
554
555 5413 berkley
                  if(dateSysMetadataModified == null)
556
                  {
557
                      startDateComparison = -1;
558
                  }
559
                  else
560
                  {
561
                      startDateComparison = dateSysMetadataModified.compareTo(startTime);
562
                  }
563
                  //System.out.println("startDateCom: " + startDateComparison);
564 5364 berkley
              }
565 5413 berkley
              else
566
              {
567
                  startDateComparison = 1;
568
              }
569 5364 berkley
570
              if(endTime != null)
571
              {
572 5427 berkley
                  Calendar zTime = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"));
573
                  zTime.setTime(endTime);
574
                  endTime = zTime.getTime();
575
576 5413 berkley
                  if(dateSysMetadataModified == null)
577
                  {
578
                      endDateComparison = 1;
579
                  }
580
                  else
581
                  {
582
                      endDateComparison = dateSysMetadataModified.compareTo(endTime);
583
                  }
584
                  //System.out.println("endDateCom: " + endDateComparison);
585 5364 berkley
              }
586 5413 berkley
              else
587
              {
588
                  endDateComparison = -1;
589
              }
590 5364 berkley
591 5413 berkley
592 5364 berkley
              if(startDateComparison < 0 || endDateComparison > 0)
593 5413 berkley
              {
594 5364 berkley
                  continue;
595
              }
596
597 5413 berkley
              docCopy.add((Document)docs.get(i));
598
          } //end pre-parse
599
600
          docs = docCopy;
601
          totalAfterQuery = docs.size();
602 5420 berkley
          System.out.println("total after subquery: " + totalAfterQuery);
603 5413 berkley
604
          //make sure we don't run over the end
605
          int end = start + count;
606
          if(end > docs.size())
607
          {
608
              end = docs.size();
609
          }
610
611
          for(int i=start; i<end; i++)
612
          {
613
              //get the document from the result
614
              Document d = (Document)docs.get(i);
615
              //System.out.println("processing doc " + d.docid);
616
617
              String dateSMM = d.getField("dateSysMetadataModified");
618
              Date dateSysMetadataModified = null;
619
              if(dateSMM != null)
620
              {
621
                  try
622
                  {
623
                      dateSysMetadataModified = parseDate(dateSMM);
624
                  }
625
                  catch(Exception e)
626
                  { //if we fail to parse the date, just ignore the value
627
                      dateSysMetadataModified = null;
628
                  }
629
              }
630
              ObjectFormat returnedObjectFormat = ObjectFormat.convert(d.getField("objectFormat"));
631
632
633 5364 berkley
              ObjectInfo info = new ObjectInfo();
634
              //add the fields to the info object
635
              Checksum cs = new Checksum();
636
              cs.setValue(d.getField("checksum"));
637 5370 berkley
              String csalg = d.getField("algorithm");
638
              if(csalg == null)
639
              {
640
                  csalg = "MD5";
641
              }
642
              ChecksumAlgorithm ca = ChecksumAlgorithm.convert(csalg);
643
              cs.setAlgorithm(ca);
644 5364 berkley
              info.setChecksum(cs);
645
              info.setDateSysMetadataModified(dateSysMetadataModified);
646
              Identifier id = new Identifier();
647
              id.setValue(d.getField("identifier"));
648
              info.setIdentifier(id);
649
              info.setObjectFormat(returnedObjectFormat);
650 5413 berkley
              String size = d.getField("size");
651
              if(size != null)
652
              {
653
                  info.setSize(new Long(size.trim()).longValue());
654
              }
655 5364 berkley
              //add the ObjectInfo to the ObjectList
656 5414 berkley
              if(info.getIdentifier().getValue() != null)
657
              { //id can be null from tests.  should not happen in production.
658
                  ol.addObjectInfo(info);
659
              }
660 5424 berkley
661 5363 berkley
          }
662 5362 berkley
      }
663
      catch(Exception e)
664
      {
665
          e.printStackTrace();
666
          throw new ServiceFailure("1580", "Error retrieving ObjectList: " + e.getMessage());
667
      }
668 5383 berkley
      String username = sessionData.getUserName();
669
      EventLog.getInstance().log(metacatUrl,
670 5384 berkley
              username, null, "read");
671 5392 berkley
      logCrud.info("listObjects");
672 5411 berkley
      //System.out.println("ol.size: " + ol.sizeObjectInfoList());
673 5413 berkley
      ol.setCount(count);
674
      ol.setStart(start);
675
      ol.setTotal(totalAfterQuery);
676 5420 berkley
      System.out.println("returning " + ol.sizeObjectInfoList() + " items in the object list");
677 5359 berkley
      return ol;
678
    }
679
680
    /**
681
     * Call listObjects with the default values for replicaStatus (true), start (0),
682
     * and count (1000).
683
     * @param token
684
     * @param startTime
685
     * @param endTime
686
     * @param objectFormat
687
     * @return
688
     * @throws NotAuthorized
689
     * @throws InvalidRequest
690
     * @throws NotImplemented
691
     * @throws ServiceFailure
692
     * @throws InvalidToken
693
     */
694
    public ObjectList listObjects(AuthToken token, Date startTime, Date endTime,
695
        ObjectFormat objectFormat)
696
      throws NotAuthorized, InvalidRequest, NotImplemented, ServiceFailure, InvalidToken
697
    {
698
       return listObjects(token, startTime, endTime, objectFormat, true, 0, 1000);
699
    }
700 5298 jones
701 5359 berkley
    /**
702
     * Delete a document.  NOT IMPLEMENTED
703
     */
704 5320 jones
    public Identifier delete(AuthToken token, Identifier guid)
705 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
706
            NotImplemented {
707 5392 berkley
        logCrud.info("delete");
708 5356 berkley
        throw new NotImplemented("1000", "This method not yet implemented.");
709 5298 jones
    }
710
711 5359 berkley
    /**
712
     * describe a document.  NOT IMPLEMENTED
713
     */
714 5320 jones
    public DescribeResponse describe(AuthToken token, Identifier guid)
715 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
716
            NotImplemented {
717 5392 berkley
        logCrud.info("describe");
718 5356 berkley
        throw new NotImplemented("1000", "This method not yet implemented.");
719 5298 jones
    }
720
721 5359 berkley
    /**
722
     * get a document with a specified guid.
723
     */
724 5320 jones
    public InputStream get(AuthToken token, Identifier guid)
725 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
726
            NotImplemented {
727
728
        // Retrieve the session information from the AuthToken
729
        // If the session is expired, then the user is 'public'
730
        final SessionData sessionData = getSessionData(token);
731
732
        // Look up the localId for this global identifier
733
        IdentifierManager im = IdentifierManager.getInstance();
734
        try {
735 5320 jones
            final String localId = im.getLocalId(guid.getValue());
736 5298 jones
737
            final InputStreamFromOutputStream<String> objectStream =
738
                new InputStreamFromOutputStream<String>() {
739
740
                @Override
741
                public String produce(final OutputStream dataSink) throws Exception {
742
743
                    try {
744 5344 berkley
                        handler.readFromMetacat(metacatUrl, null,
745 5298 jones
                                dataSink, localId, "xml",
746
                                sessionData.getUserName(),
747
                                sessionData.getGroupNames(), true, params);
748
                    } catch (PropertyNotFoundException e) {
749 5382 berkley
                        e.printStackTrace();
750
                        throw new ServiceFailure("1030", "Error getting property from metacat: " + e.getMessage());
751 5298 jones
                    } catch (ClassNotFoundException e) {
752 5382 berkley
                        e.printStackTrace();
753
                        throw new ServiceFailure("1030", "Class not found error when reading from metacat: " + e.getMessage());
754 5298 jones
                    } catch (IOException e) {
755 5382 berkley
                        e.printStackTrace();
756
                        throw new ServiceFailure("1030", "IOException while reading from metacat: " + e.getMessage());
757 5298 jones
                    } catch (SQLException e) {
758 5382 berkley
                        e.printStackTrace();
759
                        throw new ServiceFailure("1030", "SQLException while reading from metacat: " + e.getMessage());
760 5298 jones
                    } catch (McdbException e) {
761 5382 berkley
                        e.printStackTrace();
762
                        throw new ServiceFailure("1030", "Metacat DB exception while reading from metacat: " + e.getMessage());
763 5298 jones
                    } catch (ParseLSIDException e) {
764 5382 berkley
                        e.printStackTrace();
765
                        throw new NotFound("1020", "LSID parsing exception while reading from metacat: " + e.getMessage());
766 5298 jones
                    } catch (InsufficientKarmaException e) {
767 5382 berkley
                        e.printStackTrace();
768
                        throw new NotAuthorized("1000", "User not authorized for get(): " + e.getMessage());
769 5298 jones
                    }
770
771
                    return "Completed";
772
                }
773
            };
774 5427 berkley
            System.out.println("1");
775 5383 berkley
            String username = sessionData.getUserName();
776
            EventLog.getInstance().log(metacatUrl,
777 5384 berkley
                    username, im.getLocalId(guid.getValue()), "read");
778 5393 berkley
            logCrud.info("get localId:" + localId + " guid:" + guid.getValue());
779 5298 jones
            return objectStream;
780
781
        } catch (McdbDocNotFoundException e) {
782 5356 berkley
            throw new NotFound("1020", e.getMessage());
783 5298 jones
        }
784
    }
785
786 5359 berkley
    /**
787
     * get the checksum for a document.  NOT IMPLEMENTED
788
     */
789 5320 jones
    public Checksum getChecksum(AuthToken token, Identifier guid)
790 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
791
            InvalidRequest, NotImplemented {
792 5392 berkley
        logCrud.info("getChecksum");
793 5356 berkley
        throw new NotImplemented("1000", "This method not yet implemented.");
794 5298 jones
    }
795
796 5359 berkley
    /**
797
     * get the checksum for a document.  NOT IMPLEMENTED
798
     */
799 5320 jones
    public Checksum getChecksum(AuthToken token, Identifier guid,
800 5298 jones
            String checksumAlgorithm) throws InvalidToken, ServiceFailure,
801
            NotAuthorized, NotFound, InvalidRequest, NotImplemented {
802 5392 berkley
        logCrud.info("getChecksum");
803 5356 berkley
        throw new NotImplemented("1000", "This method not yet implemented.");
804 5298 jones
    }
805
806 5359 berkley
    /**
807 5384 berkley
     * get log records.
808 5359 berkley
     */
809 5390 berkley
    public Log getLogRecords(AuthToken token, Date fromDate, Date toDate, Event event)
810 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, InvalidRequest,
811 5384 berkley
            NotImplemented
812
    {
813 5389 berkley
        Log log = new Log();
814
        Vector<LogEntry> logs = new Vector<LogEntry>();
815 5384 berkley
        IdentifierManager im = IdentifierManager.getInstance();
816
        EventLog el = EventLog.getInstance();
817 5391 berkley
        if(fromDate == null)
818
        {
819 5418 berkley
            System.out.println("setting fromdate from null");
820 5391 berkley
            fromDate = new Date(1);
821
        }
822
        if(toDate == null)
823
        {
824 5418 berkley
            System.out.println("setting todate from null");
825 5391 berkley
            toDate = new Date();
826
        }
827 5418 berkley
828
        System.out.println("fromDate: " + fromDate);
829
        System.out.println("toDate: " + toDate);
830
831 5384 berkley
        String report = el.getReport(null, null, null, null,
832
                new java.sql.Timestamp(fromDate.getTime()),
833
                new java.sql.Timestamp(toDate.getTime()));
834
835 5418 berkley
        //System.out.println("report: " + report);
836
837 5384 berkley
        String logEntry = "<logEntry>";
838
        String endLogEntry = "</logEntry>";
839
        int startIndex = 0;
840
        int foundIndex = report.indexOf(logEntry, startIndex);
841
        while(foundIndex != -1)
842
        {
843
            //parse out each entry
844
            int endEntryIndex = report.indexOf(endLogEntry, foundIndex);
845
            String entry = report.substring(foundIndex, endEntryIndex);
846
            //System.out.println("entry: " + entry);
847
            startIndex = endEntryIndex + endLogEntry.length();
848
            foundIndex = report.indexOf(logEntry, startIndex);
849
850
            String entryId = getLogEntryField("entryid", entry);
851
            String ipAddress = getLogEntryField("ipAddress", entry);
852
            String principal = getLogEntryField("principal", entry);
853
            String docid = getLogEntryField("docid", entry);
854 5390 berkley
            String eventS = getLogEntryField("event", entry);
855 5384 berkley
            String dateLogged = getLogEntryField("dateLogged", entry);
856
857 5389 berkley
            LogEntry le = new LogEntry();
858 5384 berkley
859 5390 berkley
            Event e = Event.convert(eventS);
860 5384 berkley
            if(e == null)
861
            { //skip any events that are not Dataone Crud events
862
                continue;
863
            }
864 5389 berkley
            le.setEvent(e);
865 5384 berkley
            Identifier entryid = new Identifier();
866
            entryid.setValue(entryId);
867 5389 berkley
            le.setEntryId(entryid);
868 5384 berkley
            Identifier identifier = new Identifier();
869
            try
870
            {
871 5419 berkley
                System.out.println("converting docid '" + docid + "' to a guid.");
872 5417 berkley
                if(docid == null || docid.trim().equals("") || docid.trim().equals("null"))
873
                {
874
                    continue;
875
                }
876 5416 berkley
                docid = docid.substring(0, docid.lastIndexOf("."));
877 5384 berkley
                identifier.setValue(im.getGUID(docid, im.getLatestRevForLocalId(docid)));
878
            }
879
            catch(Exception ex)
880
            { //try to get the guid, if that doesn't work, just use the local id
881 5419 berkley
                //throw new ServiceFailure("1030", "Error getting guid for localId " +
882 5424 berkley
                //        docid + ": " + ex.getMessage());\
883
884
                //skip it if the guid can't be found
885 5419 berkley
                continue;
886 5384 berkley
            }
887
888 5389 berkley
            le.setIdentifier(identifier);
889
            le.setIpAddress(ipAddress);
890 5384 berkley
            Calendar c = Calendar.getInstance();
891
            String year = dateLogged.substring(0, 4);
892
            String month = dateLogged.substring(5, 7);
893
            String date = dateLogged.substring(8, 10);
894
            //System.out.println("year: " + year + " month: " + month + " day: " + date);
895
            c.set(new Integer(year).intValue(), new Integer(month).intValue(), new Integer(date).intValue());
896
            Date logDate = c.getTime();
897 5389 berkley
            le.setDateLogged(logDate);
898 5384 berkley
            NodeReference memberNode = new NodeReference();
899
            memberNode.setValue(ipAddress);
900 5389 berkley
            le.setMemberNode(memberNode);
901 5384 berkley
            Principal princ = new Principal();
902
            princ.setValue(principal);
903 5389 berkley
            le.setPrincipal(princ);
904
            le.setUserAgent("metacat/RESTService");
905 5390 berkley
906
            if(event == null)
907
            {
908
                logs.add(le);
909
            }
910
911
            if(event != null &&
912
               e.toString().toLowerCase().trim().equals(event.toString().toLowerCase().trim()))
913
            {
914
              logs.add(le);
915
            }
916 5384 berkley
        }
917
918 5389 berkley
        log.setLogEntryList(logs);
919 5392 berkley
        logCrud.info("getLogRecords");
920 5389 berkley
        return log;
921 5298 jones
    }
922 5384 berkley
923
    /**
924
     * parse a logEntry and get the relavent field from it
925
     * @param fieldname
926
     * @param entry
927
     * @return
928
     */
929
    private String getLogEntryField(String fieldname, String entry)
930
    {
931
        String begin = "<" + fieldname + ">";
932
        String end = "</" + fieldname + ">";
933
        //System.out.println("looking for " + begin + " and " + end + " in entry " + entry);
934
        String s = entry.substring(entry.indexOf(begin) + begin.length(), entry.indexOf(end));
935
        //System.out.println("entry " + fieldname + " : " + s);
936
        return s;
937
    }
938 5298 jones
939 5359 berkley
    /**
940
     * get the system metadata for a document with a specified guid.
941
     */
942 5320 jones
    public SystemMetadata getSystemMetadata(AuthToken token, Identifier guid)
943 5298 jones
            throws InvalidToken, ServiceFailure, NotAuthorized, NotFound,
944
            InvalidRequest, NotImplemented {
945 5319 jones
946 5332 jones
        logMetacat.debug("CrudService.getSystemMetadata - for guid: " + guid.getValue());
947
948
        // Retrieve the session information from the AuthToken
949
        // If the session is expired, then the user is 'public'
950
        final SessionData sessionData = getSessionData(token);
951
952
        try {
953
            IdentifierManager im = IdentifierManager.getInstance();
954 5424 berkley
            final String localId = im.getSystemMetadataLocalId(guid.getValue());
955 5332 jones
956
            // Read system metadata from metacat's db
957
            final InputStreamFromOutputStream<String> objectStream =
958
                new InputStreamFromOutputStream<String>() {
959
960
                @Override
961
                public String produce(final OutputStream dataSink) throws Exception {
962
                    try {
963 5344 berkley
                        handler.readFromMetacat(metacatUrl, null,
964 5332 jones
                                dataSink, localId, "xml",
965
                                sessionData.getUserName(),
966
                                sessionData.getGroupNames(), true, params);
967
                    } catch (PropertyNotFoundException e) {
968 5382 berkley
                        e.printStackTrace();
969
                        throw new ServiceFailure("1030", "Property not found while reading system metadata from metacat: " + e.getMessage());
970 5332 jones
                    } catch (ClassNotFoundException e) {
971 5382 berkley
                        e.printStackTrace();
972
                        throw new ServiceFailure("1030", "Class not found while reading system metadata from metacat: " + e.getMessage());
973 5332 jones
                    } catch (IOException e) {
974 5382 berkley
                        e.printStackTrace();
975
                        throw new ServiceFailure("1030", "IOException while reading system metadata from metacat: " + e.getMessage());
976 5332 jones
                    } catch (SQLException e) {
977 5382 berkley
                        e.printStackTrace();
978
                        throw new ServiceFailure("1030", "SQLException while reading system metadata from metacat: " + e.getMessage());
979 5332 jones
                    } catch (McdbException e) {
980 5382 berkley
                        e.printStackTrace();
981
                        throw new ServiceFailure("1030", "Metacat DB Exception while reading system metadata from metacat: " + e.getMessage());
982 5332 jones
                    } catch (ParseLSIDException e) {
983 5382 berkley
                        e.printStackTrace();
984
                        throw new NotFound("1020", "Error parsing LSID while reading system metadata from metacat: " + e.getMessage());
985 5332 jones
                    } catch (InsufficientKarmaException e) {
986 5382 berkley
                        e.printStackTrace();
987
                        throw new NotAuthorized("1000", "User not authorized for get() on system metadata: " + e.getMessage());
988 5332 jones
                    }
989
990
                    return "Completed";
991
                }
992
            };
993
994
            // Deserialize the xml to create a SystemMetadata object
995
            SystemMetadata sysmeta = deserializeSystemMetadata(objectStream);
996 5383 berkley
            String username = sessionData.getUserName();
997
            EventLog.getInstance().log(metacatUrl,
998 5384 berkley
                    username, im.getLocalId(guid.getValue()), "read");
999 5393 berkley
            logCrud.info("getSystemMetadata localId: " + localId + " guid:" + guid.getValue());
1000 5332 jones
            return sysmeta;
1001
1002
        } catch (McdbDocNotFoundException e) {
1003 5348 berkley
            //e.printStackTrace();
1004 5356 berkley
            throw new NotFound("1000", e.getMessage());
1005 5332 jones
        }
1006 5298 jones
    }
1007 5366 berkley
1008
    /**
1009
     * parse the date in the systemMetadata
1010
     * @param s
1011
     * @return
1012
     * @throws Exception
1013
     */
1014 5418 berkley
    public Date parseDate(String s)
1015 5366 berkley
      throws Exception
1016
    {
1017
        Date d = null;
1018
        int tIndex = s.indexOf("T");
1019
        int zIndex = s.indexOf("Z");
1020
        if(tIndex != -1 && zIndex != -1)
1021
        { //parse a date that looks like 2010-05-18T21:12:54.362Z
1022
            //System.out.println("original date: " + s);
1023
1024
            String date = s.substring(0, tIndex);
1025
            String year = date.substring(0, date.indexOf("-"));
1026
            String month = date.substring(date.indexOf("-") + 1, date.lastIndexOf("-"));
1027
            String day = date.substring(date.lastIndexOf("-") + 1, date.length());
1028
            /*System.out.println("date: " + "year: " + new Integer(year).intValue() +
1029
                    " month: " + new Integer(month).intValue() + " day: " +
1030
                    new Integer(day).intValue());
1031
            */
1032
            String time = s.substring(tIndex + 1, zIndex);
1033
            String hour = time.substring(0, time.indexOf(":"));
1034
            String minute = time.substring(time.indexOf(":") + 1, time.lastIndexOf(":"));
1035
            String seconds = "00";
1036
            String milliseconds = "00";
1037
            if(time.indexOf(".") != -1)
1038
            {
1039
                seconds = time.substring(time.lastIndexOf(":") + 1, time.indexOf("."));
1040
                milliseconds = time.substring(time.indexOf(".") + 1, time.length());
1041
            }
1042 5418 berkley
            else
1043
            {
1044
                seconds = time.substring(time.lastIndexOf(":") + 1, time.length());
1045
            }
1046 5366 berkley
            /*System.out.println("time: " + "hour: " + new Integer(hour).intValue() +
1047
                    " minute: " + new Integer(minute).intValue() + " seconds: " +
1048
                    new Integer(seconds).intValue() + " milli: " +
1049
                    new Integer(milliseconds).intValue());*/
1050
1051
            //d = DateFormat.getDateTimeInstance().parse(date + " " + time);
1052 5418 berkley
            Calendar c = Calendar.getInstance(/*TimeZone.getTimeZone("GMT-0")*/TimeZone.getDefault());
1053 5366 berkley
            c.set(new Integer(year).intValue(), new Integer(month).intValue() - 1,
1054
                  new Integer(day).intValue(), new Integer(hour).intValue(),
1055
                  new Integer(minute).intValue(), new Integer(seconds).intValue());
1056
            c.set(Calendar.MILLISECOND, new Integer(milliseconds).intValue());
1057
            d = new Date(c.getTimeInMillis());
1058
            //System.out.println("d: " + d);
1059
            return d;
1060
        }
1061
        else
1062
        {  //if it's not in the expected format, try the formatter
1063
            return DateFormat.getDateTimeInstance().parse(s);
1064
        }
1065
    }
1066 5298 jones
1067 5331 jones
    /*
1068
     * Look up the information on the session using the token provided in
1069
     * the AuthToken.  The Session should have all relevant user information.
1070
     * If the session has expired or is invalid, the 'public' session will
1071
     * be returned, giving the user anonymous access.
1072
     */
1073 5394 berkley
    public static SessionData getSessionData(AuthToken token) {
1074 5331 jones
        SessionData sessionData = null;
1075
        String sessionId = "PUBLIC";
1076
        if (token != null) {
1077
            sessionId = token.getToken();
1078
        }
1079
1080
        // if the session id is registered in SessionService, get the
1081
        // SessionData for it. Otherwise, use the public session.
1082 5411 berkley
        //System.out.println("sessionid: " + sessionId);
1083 5403 berkley
        if (sessionId != null &&
1084 5406 berkley
            !sessionId.toLowerCase().equals("public") &&
1085 5403 berkley
            SessionService.getInstance().isSessionRegistered(sessionId))
1086
        {
1087 5374 berkley
            sessionData = SessionService.getInstance().getRegisteredSession(sessionId);
1088 5331 jones
        } else {
1089 5374 berkley
            sessionData = SessionService.getInstance().getPublicSession();
1090 5331 jones
        }
1091 5411 berkley
1092 5331 jones
        return sessionData;
1093
    }
1094
1095
    /**
1096
     * Determine if a given object should be treated as an XML science metadata
1097
     * object.
1098
     *
1099
     * TODO: This test should be externalized in a configuration dictionary rather than being hardcoded.
1100
     *
1101
     * @param sysmeta the SystemMetadata describig the object
1102
     * @return true if the object should be treated as science metadata
1103
     */
1104
    private boolean isScienceMetadata(SystemMetadata sysmeta) {
1105
        boolean scimeta = false;
1106
        switch (sysmeta.getObjectFormat()) {
1107
            case EML_2_1_0: scimeta = true; break;
1108
            case EML_2_0_1: scimeta = true; break;
1109
            case EML_2_0_0: scimeta = true; break;
1110
            case FGDC_STD_001_1_1999: scimeta = true; break;
1111
            case FGDC_STD_001_1998: scimeta = true; break;
1112
            case NCML_2_2: scimeta = true; break;
1113
        }
1114
1115
        return scimeta;
1116
    }
1117
1118 5359 berkley
    /**
1119
     * insert a data doc
1120
     * @param object
1121
     * @param guid
1122
     * @param sessionData
1123
     * @throws ServiceFailure
1124
     */
1125 5331 jones
    private void insertDataObject(InputStream object, Identifier guid,
1126
            SessionData sessionData) throws ServiceFailure {
1127
1128
        String username = sessionData.getUserName();
1129
        String[] groups = sessionData.getGroupNames();
1130
1131
        // generate guid/localId pair for object
1132
        logMetacat.debug("Generating a guid/localId mapping");
1133
        IdentifierManager im = IdentifierManager.getInstance();
1134
        String localId = im.generateLocalId(guid.getValue(), 1);
1135
1136
        try {
1137
            logMetacat.debug("Case DATA: starting to write to disk.");
1138
            if (DocumentImpl.getDataFileLockGrant(localId)) {
1139
1140
                // Save the data file to disk using "localId" as the name
1141
                try {
1142
                    String datafilepath = PropertyService.getProperty("application.datafilepath");
1143
1144
                    File dataDirectory = new File(datafilepath);
1145
                    dataDirectory.mkdirs();
1146
1147
                    File newFile = writeStreamToFile(dataDirectory, localId, object);
1148
1149
                    // TODO: Check that the file size matches SystemMetadata
1150
                    //                        long size = newFile.length();
1151
                    //                        if (size == 0) {
1152
                    //                            throw new IOException("Uploaded file is 0 bytes!");
1153
                    //                        }
1154
1155
                    // Register the file in the database (which generates an exception
1156
                    // if the localId is not acceptable or other untoward things happen
1157
                    try {
1158
                        logMetacat.debug("Registering document...");
1159
                        DocumentImpl.registerDocument(localId, "BIN", localId,
1160
                                username, groups);
1161
                        logMetacat.debug("Registration step completed.");
1162
                    } catch (SQLException e) {
1163
                        //newFile.delete();
1164
                        logMetacat.debug("SQLE: " + e.getMessage());
1165
                        e.printStackTrace(System.out);
1166 5356 berkley
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1167 5331 jones
                    } catch (AccessionNumberException e) {
1168
                        //newFile.delete();
1169
                        logMetacat.debug("ANE: " + e.getMessage());
1170
                        e.printStackTrace(System.out);
1171 5356 berkley
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1172 5331 jones
                    } catch (Exception e) {
1173
                        //newFile.delete();
1174
                        logMetacat.debug("Exception: " + e.getMessage());
1175
                        e.printStackTrace(System.out);
1176 5356 berkley
                        throw new ServiceFailure("1190", "Registration failed: " + e.getMessage());
1177 5331 jones
                    }
1178
1179
                    logMetacat.debug("Logging the creation event.");
1180 5344 berkley
                    EventLog.getInstance().log(metacatUrl,
1181 5384 berkley
                            username, localId, "create");
1182 5331 jones
1183
                    // Schedule replication for this data file
1184
                    logMetacat.debug("Scheduling replication.");
1185
                    ForceReplicationHandler frh = new ForceReplicationHandler(
1186 5384 berkley
                            localId, "create", false, null);
1187 5331 jones
1188
                } catch (PropertyNotFoundException e) {
1189 5356 berkley
                    throw new ServiceFailure("1190", "Could not lock file for writing:" + e.getMessage());
1190 5331 jones
                }
1191
1192
            }
1193
        } catch (Exception e) {
1194
            // Could not get a lock on the document, so we can not update the file now
1195 5356 berkley
            throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
1196 5331 jones
        }
1197
    }
1198
1199 5359 berkley
    /**
1200
     * write a file to a stream
1201
     * @param dir
1202
     * @param fileName
1203
     * @param data
1204
     * @return
1205
     * @throws ServiceFailure
1206
     */
1207 5319 jones
    private File writeStreamToFile(File dir, String fileName, InputStream data)
1208
        throws ServiceFailure {
1209
1210
        File newFile = new File(dir, fileName);
1211
        logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
1212
1213
        try {
1214
            if (newFile.createNewFile()) {
1215
                // write data stream to desired file
1216
                OutputStream os = new FileOutputStream(newFile);
1217
                long length = IOUtils.copyLarge(data, os);
1218
                os.flush();
1219
                os.close();
1220
            } else {
1221
                logMetacat.debug("File creation failed, or file already exists.");
1222 5356 berkley
                throw new ServiceFailure("1190", "File already exists: " + fileName);
1223 5319 jones
            }
1224
        } catch (FileNotFoundException e) {
1225
            logMetacat.debug("FNF: " + e.getMessage());
1226 5356 berkley
            throw new ServiceFailure("1190", "File not found: " + fileName + " "
1227 5319 jones
                    + e.getMessage());
1228
        } catch (IOException e) {
1229
            logMetacat.debug("IOE: " + e.getMessage());
1230 5356 berkley
            throw new ServiceFailure("1190", "File was not written: " + fileName
1231 5319 jones
                    + " " + e.getMessage());
1232
        }
1233
1234
        return newFile;
1235 5329 jones
    }
1236 5420 berkley
1237
    private static Date getDateInTimeZone(Date currentDate, String timeZoneId)
1238
    {
1239
        TimeZone tz = TimeZone.getTimeZone(timeZoneId);
1240
        Calendar mbCal = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId));
1241
        mbCal.setTimeInMillis(currentDate.getTime());
1242 5329 jones
1243 5420 berkley
        Calendar cal = Calendar.getInstance();
1244
        cal.set(Calendar.YEAR, mbCal.get(Calendar.YEAR));
1245
        cal.set(Calendar.MONTH, mbCal.get(Calendar.MONTH));
1246
        cal.set(Calendar.DAY_OF_MONTH, mbCal.get(Calendar.DAY_OF_MONTH));
1247
        cal.set(Calendar.HOUR_OF_DAY, mbCal.get(Calendar.HOUR_OF_DAY));
1248
        cal.set(Calendar.MINUTE, mbCal.get(Calendar.MINUTE));
1249
        cal.set(Calendar.SECOND, mbCal.get(Calendar.SECOND));
1250
        cal.set(Calendar.MILLISECOND, mbCal.get(Calendar.MILLISECOND));
1251
1252
        return cal.getTime();
1253
    }
1254
1255 5350 berkley
    /**
1256
     * insert a systemMetadata doc
1257
     */
1258 5329 jones
    private void insertSystemMetadata(SystemMetadata sysmeta, SessionData sessionData)
1259 5377 berkley
        throws ServiceFailure
1260
    {
1261 5329 jones
        logMetacat.debug("Starting to insert SystemMetadata...");
1262
1263
        // generate guid/localId pair for sysmeta
1264 5331 jones
        Identifier sysMetaGuid = new Identifier();
1265
        sysMetaGuid.setValue(DocumentUtil.generateDocumentId(1));
1266 5329 jones
        sysmeta.setDateSysMetadataModified(new Date());
1267 5420 berkley
        System.out.println("****inserting new system metadata with modified date " + sysmeta.getDateSysMetadataModified());
1268 5329 jones
1269
        String xml = new String(serializeSystemMetadata(sysmeta).toByteArray());
1270 5333 berkley
        String localId = insertDocument(xml, sysMetaGuid, sessionData);
1271 5420 berkley
        System.out.println("sysmeta inserted with localId " + localId);
1272 5333 berkley
        //insert the system metadata doc id into the identifiers table to
1273
        //link it to the data or metadata document
1274 5377 berkley
        IdentifierManager.getInstance().createSystemMetadataMapping(
1275
                sysmeta.getIdentifier().getValue(), sysMetaGuid.getValue());
1276 5329 jones
    }
1277
1278 5333 berkley
    /**
1279 5350 berkley
     * update a systemMetadata doc
1280
     */
1281
    private void updateSystemMetadata(SystemMetadata sm, SessionData sessionData)
1282
      throws ServiceFailure
1283
    {
1284
        try
1285
        {
1286 5424 berkley
            String smId = IdentifierManager.getInstance().getSystemMetadataLocalId(sm.getIdentifier().getValue());
1287 5350 berkley
            sm.setDateSysMetadataModified(new Date());
1288
            String xml = new String(serializeSystemMetadata(sm).toByteArray());
1289
            Identifier id = new Identifier();
1290
            id.setValue(smId);
1291
            String localId = updateDocument(xml, id, null, sessionData);
1292
            IdentifierManager.getInstance().updateSystemMetadataMapping(sm.getIdentifier().getValue(), localId);
1293
        }
1294
        catch(Exception e)
1295
        {
1296 5356 berkley
            throw new ServiceFailure("1030", "Error updating system metadata: " + e.getMessage());
1297 5350 berkley
        }
1298
    }
1299
1300
    /**
1301
     * insert a document
1302
     * NOTE: this method shouldn't be used from the update or create() methods.
1303
     * we shouldn't be putting the science metadata or data objects into memory.
1304
     */
1305
    private String insertDocument(String xml, Identifier guid, SessionData sessionData)
1306
        throws ServiceFailure
1307
    {
1308
        return insertOrUpdateDocument(xml, guid, sessionData, "insert");
1309
    }
1310
1311
    /**
1312
     * insert a document from a stream
1313
     */
1314
    private String insertDocument(InputStream is, Identifier guid, SessionData sessionData)
1315
      throws IOException, ServiceFailure
1316
    {
1317
        //HACK: change this eventually.  we should not be converting the stream to a string
1318
        String xml = IOUtils.toString(is);
1319
        return insertDocument(xml, guid, sessionData);
1320
    }
1321
1322
    /**
1323
     * update a document
1324
     * NOTE: this method shouldn't be used from the update or create() methods.
1325
     * we shouldn't be putting the science metadata or data objects into memory.
1326
     */
1327
    private String updateDocument(String xml, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
1328
        throws ServiceFailure
1329
    {
1330
        return insertOrUpdateDocument(xml, obsoleteGuid, sessionData, "update");
1331
    }
1332
1333
    /**
1334
     * update a document from a stream
1335
     */
1336
    private String updateDocument(InputStream is, Identifier obsoleteGuid, Identifier guid, SessionData sessionData)
1337
      throws IOException, ServiceFailure
1338
    {
1339
        //HACK: change this eventually.  we should not be converting the stream to a string
1340
        String xml = IOUtils.toString(is);
1341
        String localId = updateDocument(xml, obsoleteGuid, guid, sessionData);
1342
        IdentifierManager im = IdentifierManager.getInstance();
1343
        if(guid != null)
1344
        {
1345
          im.createMapping(guid.getValue(), localId);
1346
        }
1347
        return localId;
1348
    }
1349
1350
    /**
1351 5333 berkley
     * insert a document, return the id of the document that was inserted
1352
     */
1353 5380 berkley
    protected String insertOrUpdateDocument(String xml, Identifier guid, SessionData sessionData, String insertOrUpdate)
1354 5329 jones
        throws ServiceFailure {
1355
        logMetacat.debug("Starting to insert xml document...");
1356
        IdentifierManager im = IdentifierManager.getInstance();
1357
1358
        // generate guid/localId pair for sysmeta
1359 5350 berkley
        String localId = null;
1360
        if(insertOrUpdate.equals("insert"))
1361
        {
1362
            localId = im.generateLocalId(guid.getValue(), 1);
1363
        }
1364
        else
1365
        {
1366
            //localid should already exist in the identifier table, so just find it
1367
            try
1368
            {
1369
                localId = im.getLocalId(guid.getValue());
1370
                //increment the revision
1371
                String docid = localId.substring(0, localId.lastIndexOf("."));
1372
                String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
1373
                int rev = new Integer(revS).intValue();
1374
                rev++;
1375
                docid = docid + "." + rev;
1376
                localId = docid;
1377
            }
1378
            catch(McdbDocNotFoundException e)
1379
            {
1380 5356 berkley
                throw new ServiceFailure("1030", "CrudService.insertOrUpdateDocument(): " +
1381 5350 berkley
                    "guid " + guid.getValue() + " should have been in the identifier table, but it wasn't: " + e.getMessage());
1382
            }
1383
        }
1384 5331 jones
        logMetacat.debug("Metadata guid|localId: " + guid.getValue() + "|" +
1385 5329 jones
                localId);
1386
1387
        String[] action = new String[1];
1388 5350 berkley
        action[0] = insertOrUpdate;
1389 5329 jones
        params.put("action", action);
1390
        String[] docid = new String[1];
1391
        docid[0] = localId;
1392
        params.put("docid", docid);
1393
        String[] doctext = new String[1];
1394
        doctext[0] = xml;
1395
        logMetacat.debug(doctext[0]);
1396
        params.put("doctext", doctext);
1397 5331 jones
1398
        // TODO: refactor handleInsertOrUpdateAction() to not output XML directly
1399
        // onto output stream, or alternatively, capture that and parse it to
1400
        // generate the right exceptions
1401 5337 berkley
        //ByteArrayOutputStream output = new ByteArrayOutputStream();
1402
        //PrintWriter pw = new PrintWriter(output);
1403 5344 berkley
        String result = handler.handleInsertOrUpdateAction(metacatUrl, null,
1404 5337 berkley
                            null, params, sessionData.getUserName(), sessionData.getGroupNames());
1405
        //String outputS = new String(output.toByteArray());
1406
        logMetacat.debug("CrudService.insertDocument - Metacat returned: " + result);
1407 5333 berkley
        logMetacat.debug("Finsished inserting xml document with id " + localId);
1408
        return localId;
1409 5329 jones
    }
1410
1411 5359 berkley
    /**
1412
     * serialize a system metadata doc
1413
     * @param sysmeta
1414
     * @return
1415
     * @throws ServiceFailure
1416
     */
1417 5332 jones
    public static ByteArrayOutputStream serializeSystemMetadata(SystemMetadata sysmeta)
1418 5329 jones
        throws ServiceFailure {
1419
        IBindingFactory bfact;
1420
        ByteArrayOutputStream sysmetaOut = null;
1421
        try {
1422
            bfact = BindingDirectory.getFactory(SystemMetadata.class);
1423
            IMarshallingContext mctx = bfact.createMarshallingContext();
1424
            sysmetaOut = new ByteArrayOutputStream();
1425
            mctx.marshalDocument(sysmeta, "UTF-8", null, sysmetaOut);
1426
        } catch (JiBXException e) {
1427 5382 berkley
            e.printStackTrace();
1428 5356 berkley
            throw new ServiceFailure("1190", "Failed to serialize and insert SystemMetadata: " + e.getMessage());
1429 5329 jones
        }
1430
1431
        return sysmetaOut;
1432
    }
1433 5332 jones
1434 5359 berkley
    /**
1435
     * deserialize a system metadata doc
1436
     * @param xml
1437
     * @return
1438
     * @throws ServiceFailure
1439
     */
1440 5332 jones
    public static SystemMetadata deserializeSystemMetadata(InputStream xml)
1441
        throws ServiceFailure {
1442
        try {
1443
            IBindingFactory bfact = BindingDirectory.getFactory(SystemMetadata.class);
1444
            IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
1445
            SystemMetadata sysmeta = (SystemMetadata) uctx.unmarshalDocument(xml, null);
1446
            return sysmeta;
1447
        } catch (JiBXException e) {
1448 5382 berkley
            e.printStackTrace();
1449
            throw new ServiceFailure("1190", "Failed to deserialize and insert SystemMetadata: " + e.getMessage());
1450 5332 jones
        }
1451
    }
1452 5378 berkley
1453
    /**
1454
     * produce an md5 checksum for item
1455
     */
1456
    private String checksum(InputStream is)
1457
      throws Exception
1458
    {
1459
        byte[] buffer = new byte[1024];
1460
        MessageDigest complete = MessageDigest.getInstance("MD5");
1461
        int numRead;
1462
1463
        do
1464
        {
1465
          numRead = is.read(buffer);
1466
          if (numRead > 0)
1467
          {
1468
            complete.update(buffer, 0, numRead);
1469
          }
1470
        } while (numRead != -1);
1471
1472
1473
        return getHex(complete.digest());
1474
    }
1475
1476
    /**
1477
     * convert a byte array to a hex string
1478
     */
1479
    private static String getHex( byte [] raw )
1480
    {
1481
        final String HEXES = "0123456789ABCDEF";
1482
        if ( raw == null ) {
1483
          return null;
1484
        }
1485
        final StringBuilder hex = new StringBuilder( 2 * raw.length );
1486
        for ( final byte b : raw ) {
1487
          hex.append(HEXES.charAt((b & 0xF0) >> 4))
1488
             .append(HEXES.charAt((b & 0x0F)));
1489
        }
1490
        return hex.toString();
1491
    }
1492
1493
    /**
1494
     * parse the metacat date which looks like 2010-06-08 (YYYY-MM-DD) into
1495
     * a proper date object
1496
     * @param date
1497
     * @return
1498
     */
1499
    private Date parseMetacatDate(String date)
1500
    {
1501
        String year = date.substring(0, 4);
1502
        String month = date.substring(5, 7);
1503
        String day = date.substring(8, 10);
1504 5420 berkley
        Calendar c = Calendar.getInstance(TimeZone.getDefault());
1505 5378 berkley
        c.set(new Integer(year).intValue(),
1506
              new Integer(month).intValue(),
1507
              new Integer(day).intValue());
1508 5420 berkley
        System.out.println("time in parseMetacatDate: " + c.getTime());
1509 5378 berkley
        return c.getTime();
1510
    }
1511
1512
    /**
1513
     * find the size (in bytes) of a stream
1514
     * @param is
1515
     * @return
1516
     * @throws IOException
1517
     */
1518
    private long sizeOfStream(InputStream is)
1519
        throws IOException
1520
    {
1521
        long size = 0;
1522
        byte[] b = new byte[1024];
1523
        int numread = is.read(b, 0, 1024);
1524
        while(numread != -1)
1525
        {
1526
            size += numread;
1527
            numread = is.read(b, 0, 1024);
1528
        }
1529
        return size;
1530
    }
1531 5379 berkley
1532
    /**
1533
     * create system metadata with a specified id, doc and format
1534
     */
1535
    private SystemMetadata createSystemMetadata(String localId, AuthToken token)
1536
      throws Exception
1537
    {
1538
        IdentifierManager im = IdentifierManager.getInstance();
1539
        Hashtable<String, String> docInfo = im.getDocumentInfo(localId);
1540
1541
        //get the document text
1542
        int rev = im.getLatestRevForLocalId(localId);
1543
        Identifier identifier = new Identifier();
1544
        identifier.setValue(im.getGUID(localId, rev));
1545
        InputStream is = this.get(token, identifier);
1546
1547
        SystemMetadata sm = new SystemMetadata();
1548
        //set the id
1549
        sm.setIdentifier(identifier);
1550
1551
        //set the object format
1552
        String doctype = docInfo.get("doctype");
1553
        ObjectFormat format = ObjectFormat.convert(docInfo.get("doctype"));
1554
        if(format == null)
1555
        {
1556
            if(doctype.trim().equals("BIN"))
1557
            {
1558
                format = ObjectFormat.APPLICATIONOCTETSTREAM;
1559
            }
1560
            else
1561
            {
1562
                format = ObjectFormat.convert("text/plain");
1563
            }
1564
        }
1565
        sm.setObjectFormat(format);
1566
1567
        //create the checksum
1568
        String checksumS = checksum(is);
1569
        ChecksumAlgorithm ca = ChecksumAlgorithm.convert("MD5");
1570
        Checksum checksum = new Checksum();
1571
        checksum.setValue(checksumS);
1572
        checksum.setAlgorithm(ca);
1573
        sm.setChecksum(checksum);
1574
1575
        //set the size
1576
        is = this.get(token, identifier);
1577
        sm.setSize(sizeOfStream(is));
1578
1579
        //submitter
1580
        Principal p = new Principal();
1581
        p.setValue(docInfo.get("user_owner"));
1582
        sm.setSubmitter(p);
1583
        sm.setRightsHolder(p);
1584
        try
1585
        {
1586
            Date dateCreated = parseMetacatDate(docInfo.get("date_created"));
1587
            sm.setDateUploaded(dateCreated);
1588
            Date dateUpdated = parseMetacatDate(docInfo.get("date_updated"));
1589
            sm.setDateSysMetadataModified(dateUpdated);
1590
        }
1591
        catch(Exception e)
1592
        {
1593
            System.out.println("couldn't parse a date: " + e.getMessage());
1594
            Date dateCreated = new Date();
1595
            sm.setDateUploaded(dateCreated);
1596
            Date dateUpdated = new Date();
1597
            sm.setDateSysMetadataModified(dateUpdated);
1598
        }
1599
        NodeReference nr = new NodeReference();
1600
        nr.setValue("metacat");
1601
        sm.setOriginMemberNode(nr);
1602
        sm.setAuthoritativeMemberNode(nr);
1603
        return sm;
1604
    }
1605 5298 jones
}