Project

General

Profile

« Previous | Next » 

Revision 6241

Implement [MN|CN]Storage.create() in D1NodeService. Since MetacatHandler requires an IP for event logging, we pass in the metacat URL (hold over from CrudService). To do this in the abstract D1NodeService, change the constructors to take metacatUrl as a parameter and get the URL from the metacat properties file in getInstance() of the subclasses. Needs testing.

View differences:

D1NodeService.java
29 29
import java.io.FileOutputStream;
30 30
import java.io.IOException;
31 31
import java.io.InputStream;
32
import java.io.OutputStream;
32 33
import java.sql.SQLException;
33 34
import java.util.ArrayList;
34 35
import java.util.Calendar;
......
42 43

  
43 44
import javax.servlet.http.HttpServletRequest;
44 45

  
46
import org.apache.commons.io.IOUtils;
45 47
import org.apache.log4j.Logger;
48
import org.dataone.client.ObjectFormatCache;
49
import org.dataone.service.exceptions.IdentifierNotUnique;
50
import org.dataone.service.exceptions.InsufficientResources;
46 51
import org.dataone.service.exceptions.InvalidRequest;
52
import org.dataone.service.exceptions.InvalidSystemMetadata;
47 53
import org.dataone.service.exceptions.InvalidToken;
48 54
import org.dataone.service.exceptions.NotAuthorized;
49 55
import org.dataone.service.exceptions.NotFound;
50 56
import org.dataone.service.exceptions.NotImplemented;
51 57
import org.dataone.service.exceptions.ServiceFailure;
58
import org.dataone.service.exceptions.UnsupportedType;
52 59
import org.dataone.service.types.AccessPolicy;
53 60
import org.dataone.service.types.AccessRule;
54 61
import org.dataone.service.types.Event;
......
57 64
import org.dataone.service.types.Log;
58 65
import org.dataone.service.types.LogEntry;
59 66
import org.dataone.service.types.NodeReference;
67
import org.dataone.service.types.ObjectFormat;
60 68
import org.dataone.service.types.Permission;
61 69
import org.dataone.service.types.Person;
62 70
import org.dataone.service.types.Session;
63 71
import org.dataone.service.types.Subject;
64 72
import org.dataone.service.types.SystemMetadata;
65 73

  
74
import edu.ucsb.nceas.metacat.AccessionNumberException;
75
import edu.ucsb.nceas.metacat.DocumentImpl;
66 76
import edu.ucsb.nceas.metacat.EventLog;
67 77
import edu.ucsb.nceas.metacat.IdentifierManager;
68 78
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
......
70 80
import edu.ucsb.nceas.metacat.MetacatHandler;
71 81
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
72 82
import edu.ucsb.nceas.metacat.properties.PropertyService;
83
import edu.ucsb.nceas.metacat.replication.ForceReplicationHandler;
84
import edu.ucsb.nceas.metacat.util.SessionData;
73 85
import edu.ucsb.nceas.utilities.ParseLSIDException;
74 86
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
75 87

  
......
77 89
  
78 90
  private static Logger logMetacat = Logger.getLogger(D1NodeService.class);
79 91

  
92
  /* the metacat URL as a string (including ending /d1 service)*/
93
  private String metacatUrl;
94
  
80 95
  /* reference to the metacat handler */
81 96
  private MetacatHandler handler;
82 97
  
83 98
  /* parameters set in the incoming request */
84 99
  private Hashtable<String, String[]> params;
85 100

  
86
  /* Methods common to CNCore and MNCore APIs */
101
  /**
102
   * Constructor - used to set the metacatUrl from a subclass extending D1NodeService
103
   * 
104
   * @param metacatUrl - the URL of the metacat service, including the ending /d1
105
   */
106
  public D1NodeService(String metacatUrl) {
107
    this.metacatUrl = metacatUrl;
108
    
109
  }
110
  
111
  /**
112
   * Adds a new object to the Node, where the object is either a data 
113
   * object or a science metadata object. This method is called by clients 
114
   * to create new data objects on Member Nodes or internally for Coordinating
115
   * Nodes
116
   * 
117
   * @param session - the Session object containing the credentials for the Subject
118
   * @param pid - The object identifier to be created
119
   * @param object - the object bytes
120
   * @param sysmeta - the system metadata that describes the object  
121
   * 
122
   * @return pid - the object identifier created
123
   * 
124
   * @throws InvalidToken
125
   * @throws ServiceFailure
126
   * @throws NotAuthorized
127
   * @throws IdentifierNotUnique
128
   * @throws UnsupportedType
129
   * @throws InsufficientResources
130
   * @throws InvalidSystemMetadata
131
   * @throws NotImplemented
132
   * @throws InvalidRequest
133
   */
134
  public Identifier create(Session session, Identifier pid, InputStream object,
135
    SystemMetadata sysmeta) 
136
    throws InvalidToken, ServiceFailure, NotAuthorized, IdentifierNotUnique, 
137
    UnsupportedType, InsufficientResources, InvalidSystemMetadata, 
138
    NotImplemented, InvalidRequest {
87 139

  
140
    Identifier resultPid = null;
141
    String localId = null;
142
    Subject subject = session.getSubject();
143
    boolean allowed = false;
144
    
145
    // be sure the user is authenticated for create()
146
    if (subject.getValue() == null || 
147
        subject.getValue().toLowerCase().equals("public") ) {
148
      throw new NotAuthorized("1100", "The provided identity does not have " +
149
        "permission to WRITE to the Member Node.");
150
      
151
    }
152
    
153
    // verify that pid == SystemMetadata.getIdentifier()
154
    logMetacat.debug("Comparing pid|sysmeta_pid: " + 
155
      pid.getValue() + "|" + sysmeta.getIdentifier().getValue());
156
    if (!pid.getValue().equals(sysmeta.getIdentifier().getValue())) {
157
        throw new InvalidSystemMetadata("1180", 
158
            "The supplied system metadata is invalid. " +
159
            "The identifier " + pid.getValue() + " does not match identifier" +
160
            "in the system metadata identified by " +
161
            sysmeta.getIdentifier().getValue() + ".");
162
        
163
    }
164

  
165
    logMetacat.debug("Checking if identifier exists...");
166
    // Check that the identifier does not already exist
167
    IdentifierManager im = IdentifierManager.getInstance();
168
    if (im.identifierExists(pid.getValue())) {
169
        throw new IdentifierNotUnique("1120", 
170
          "The requested identifier " + pid.getValue() +
171
          " is already used by another object and" +
172
          "therefore can not be used for this object. Clients should choose" +
173
          "a new identifier that is unique and retry the operation or " +
174
          "use CN_crud.reserveIdentifier() to reserve one.");
175
        
176
    }
177

  
178
    // check for permission
179
    try {
180
      allowed = isAuthorized(session, pid, Permission.WRITE);
181
            
182
    } catch (NotFound e) {
183
      // The identifier doesn't exist, writing should be fine.
184
      allowed = true;
185
      
186
    }
187
    
188
    // we have the go ahead
189
    if ( allowed ) {
190
      
191
      // Science metadata (XML) or science data object?
192
      // TODO: there are cases where certain object formats are science metadata
193
      // but are not XML (netCDF ...).  Handle this.
194
      if ( isScienceMetadata(sysmeta) ) {
195
        
196
        // CASE METADATA:
197
      	String objectAsXML = "";
198
        try {
199
	        objectAsXML = IOUtils.toString(object, "UTF-8");
200
	        localId = insertOrUpdateDocument(objectAsXML, pid, session, "insert");
201
	        //localId = im.getLocalId(pid.getValue());
202

  
203
        } catch (IOException e) {
204
        	String msg = "The Node is unable to create the object. " +
205
          "There was a problem converting the object to XML";
206
        	logMetacat.info(msg);
207
          throw new ServiceFailure("1190", msg + ": " + e.getMessage());
208

  
209
        }
210
                    
211
      }
212
        
213

  
214
    } else {
215
        
216
      // DEFAULT CASE: DATA (needs to be checked and completed)
217
      //localId = insertDataObject(object, pid, session);
218
        
219
    }
220

  
221
    // setting the resulting identifier failed
222
    if (resultPid == null ) {
223
      throw new ServiceFailure("1190", "The Node is unable to create the object. " +
224
        "The resulting identifier was null.");
225
      
226
    }
227
    
228
    return resultPid;
229
  }
230

  
88 231
  /**
89 232
   * Return the log records associated with a given event between the start and 
90 233
   * end dates listed given a particular Subject listed in the Session
......
165 308
      le.setEntryId(entryid);
166 309
      Identifier identifier = new Identifier();
167 310
      try {
168
        logMetacat.debug("converting docid '" + docid + "' to a guid.");
311
        logMetacat.debug("converting docid '" + docid + "' to a pid.");
169 312
        if (docid == null || docid.trim().equals("") || docid.trim().equals("null")) {
170 313
          continue;
171 314
        }
172 315
        docid = docid.substring(0, docid.lastIndexOf("."));
173 316
        identifier.setValue(im.getGUID(docid, im.getLatestRevForLocalId(docid)));
174 317
      } catch (Exception ex) { 
175
        // try to get the guid, if that doesn't
318
        // try to get the pid, if that doesn't
176 319
        // work, just use the local id
177 320
        // throw new ServiceFailure("1030",
178
        // "Error getting guid for localId " +
321
        // "Error getting pid for localId " +
179 322
        // docid + ": " + ex.getMessage());\
180 323

  
181
        // skip it if the guid can't be found
324
        // skip it if the pid can't be found
182 325
        continue;
183 326
      }
184 327

  
......
216 359
    logMetacat.info("getLogRecords");
217 360
    return log;
218 361
  }
219
  
220
  /* End methods common to CNCore and MNCore APIs */
221

  
222
  /* Methods common to CNRead and MNRead APIs */
223
  
362
    
224 363
  /**
225 364
   * Return the object identified by the given object identifier
226 365
   * 
......
394 533
    
395 534
    return systemMetadata;
396 535
  }
397
  
398
  /* End methods common to CNRead and MNRead APIs */
399

  
400
  /* Methods common to CNAuthorization and MNAuthorization APIs */
401
  
536
   
402 537
  /**
403 538
   * Set access for a given object using the object identifier and a Subject
404 539
   * under a given Session.
......
425 560
    // get the subject
426 561
    Subject subject = session.getSubject();
427 562
    // get the system metadata
428
    String guid = pid.getValue();
563
    String pidStr = pid.getValue();
429 564
    
430 565
    // are we allowed to do this?
431 566
    if (!isAuthorized(session, pid, Permission.CHANGE_PERMISSION)) {
432
      throw new NotAuthorized("4420", "not allowed by " + subject.getValue() + " on " + guid);  
567
      throw new NotAuthorized("4420", "not allowed by " + subject.getValue() + " on " + pidStr);  
433 568
    }
434 569
    
435 570
    SystemMetadata systemMetadata = null;
436 571
    try {
437
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
572
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pidStr);
438 573
    } catch (McdbDocNotFoundException e) {
439
      throw new NotFound("4400", "No record found for: " + guid);
574
      throw new NotFound("4400", "No record found for: " + pid);
440 575
    }
441 576
        
442 577
    // set the access policy
......
487 622
    }
488 623
    
489 624
    // get the system metadata
490
    String guid = pid.getValue();
625
    String pidStr = pid.getValue();
491 626
    SystemMetadata systemMetadata = null;
492 627
    try {
493
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
628
      systemMetadata = IdentifierManager.getInstance().getSystemMetadata(pidStr);
494 629
    } catch (McdbDocNotFoundException e) {
495
      throw new NotFound("1800", "No record found for: " + guid);
630
      throw new NotFound("1800", "No record found for: " + pid);
496 631
    }
497 632
    List<AccessRule> allows = systemMetadata.getAccessPolicy().getAllowList();
498 633
    for (AccessRule accessRule: allows) {
......
508 643
    
509 644
    // TODO: throw or return?
510 645
    if (!allowed) {
511
      throw new NotAuthorized("1820", permission + "not allowed on " + guid);
646
      throw new NotAuthorized("1820", permission + "not allowed on " + pid);
512 647
    }
513 648
    return allowed;
649
    
514 650
  }
515

  
516
  /* End methods common to CNAuthorization and MNAuthorization APIs */
517 651
  
518 652
  /**
519 653
   * parse a logEntry and get the relevant field from it
......
550 684
    }
551 685
  }
552 686
  
687
  /** 
688
   * Determine if a given object should be treated as an XML science metadata
689
   * object. 
690
   * 
691
   * @param sysmeta - the SystemMetadata describing the object
692
   * @return true if the object should be treated as science metadata
693
   */
694
  private boolean isScienceMetadata(SystemMetadata sysmeta) {
695
    
696
    ObjectFormat objectFormat = null;
697
    boolean isScienceMetadata = false;
698
    
699
    try {
700
      objectFormat = ObjectFormatCache.getInstance().getFormat(sysmeta.getObjectFormat().getFmtid());
701
      isScienceMetadata = objectFormat.isScienceMetadata();
702
      
703
    } catch (InvalidRequest e) {
704
       logMetacat.debug("There was a problem determining if the object identified by" + 
705
         sysmeta.getIdentifier().getValue() + 
706
         " is science metadata: " + e.getMessage());
707
       
708
    } catch (ServiceFailure e) {
709
      logMetacat.debug("There was a problem determining if the object identified by" + 
710
          sysmeta.getIdentifier().getValue() + 
711
          " is science metadata: " + e.getMessage());
712
    
713
    } catch (NotFound e) {
714
      logMetacat.debug("There was a problem determining if the object identified by" + 
715
          sysmeta.getIdentifier().getValue() + 
716
          " is science metadata: " + e.getMessage());
717
    
718
    } catch (InsufficientResources e) {
719
      logMetacat.debug("There was a problem determining if the object identified by" + 
720
          sysmeta.getIdentifier().getValue() + 
721
          " is science metadata: " + e.getMessage());
722
    
723
    } catch (NotImplemented e) {
724
      logMetacat.debug("There was a problem determining if the object identified by" + 
725
          sysmeta.getIdentifier().getValue() + 
726
          " is science metadata: " + e.getMessage());
727
    
728
    }
729
    
730
    return isScienceMetadata;
553 731

  
554
}
732
  }
733
  
734
  /**
735
   * Insert or update an XML document into Metacat
736
   * 
737
   * @param xml - the XML document to insert or update
738
   * @param pid - the identifier to be used for the resulting object
739
   * 
740
   * @return localId - the resulting docid of the document created or updated
741
   * 
742
   */
743
  protected String insertOrUpdateDocument(String xml, Identifier pid, 
744
    Session session, String insertOrUpdate) 
745
    throws ServiceFailure {
746
    
747
  	logMetacat.debug("Starting to insert xml document...");
748
    IdentifierManager im = IdentifierManager.getInstance();
749

  
750
    // generate pid/localId pair for sysmeta
751
    String localId = null;
752
    
753
    if(insertOrUpdate.equals("insert")) {
754
      localId = im.generateLocalId(pid.getValue(), 1);
755
      
756
    } else {
757
      //localid should already exist in the identifier table, so just find it
758
      try {
759
        logMetacat.debug("Updating pid " + pid.getValue());
760
        logMetacat.debug("looking in identifier table for pid " + pid.getValue());
761
        
762
        localId = im.getLocalId(pid.getValue());
763
        
764
        logMetacat.debug("localId: " + localId);
765
        //increment the revision
766
        String docid = localId.substring(0, localId.lastIndexOf("."));
767
        String revS = localId.substring(localId.lastIndexOf(".") + 1, localId.length());
768
        int rev = new Integer(revS).intValue();
769
        rev++;
770
        docid = docid + "." + rev;
771
        localId = docid;
772
        logMetacat.debug("incremented localId: " + localId);
773
      
774
      } catch(McdbDocNotFoundException e) {
775
        throw new ServiceFailure("1030", "D1NodeService.insertOrUpdateDocument(): " +
776
            "pid " + pid.getValue() + 
777
            " should have been in the identifier table, but it wasn't: " + 
778
            e.getMessage());
779
      
780
      }
781
      
782
    }
783

  
784
    String[] action = new String[1];
785
    action[0] = insertOrUpdate;
786
    params.put("action", action);
787
    String[] docid = new String[1];
788
    docid[0] = localId;
789
    params.put("docid", docid);
790
    String[] doctext = new String[1];
791
    doctext[0] = xml;
792
    params.put("doctext", doctext);
793
    
794
    String username = "public";
795
    String[] groupnames = null;
796
    if( session != null ) {
797
      username = session.getSubject().getValue();
798
      List<Group> groupList = session.getSubjectList().getGroupList();
799
      for ( int i = 0; i > groupList.size(); i++ ) {
800
      	groupnames[i] = groupList.get(i).getGroupName();
801
      	
802
      }
803
    }
804
    
805
    // do the insert or update action
806
    String result = handler.handleInsertOrUpdateAction(metacatUrl, null, 
807
                        null, params, username, groupnames);
808
    
809
    if(result.indexOf("<error>") != -1) {
810
    	String detailCode = "";
811
    	if ( insertOrUpdate.equals("insert") ) {
812
    		detailCode = "1190";
813
    		
814
    	} else if ( insertOrUpdate.equals("update") ) {
815
    		detailCode = "1310";
816
    		
817
    	}
818
        throw new ServiceFailure(detailCode, 
819
          "Error inserting or updating document: " + result);
820
    }
821
    logMetacat.debug("Finsished inserting xml document with id " + localId);
822
    
823
    return localId;
824
  }
825
  
826
  /**
827
   * Insert a data document
828
   * 
829
   * @param object
+   * @param pid
+   * @param sessionData
+   * @throws ServiceFailure
+   * @returns localId of the data object inserted
+   */
+  private String insertDataObject(InputStream object, Identifier pid, 
+          Session session) throws ServiceFailure {
+      
+    String username = "public";
830
    String[] groupnames = null;
831
    if( session != null ) {
832
      username = session.getSubject().getValue();
833
      List<Group> groupList = session.getSubjectList().getGroupList();
834
      for ( int i = 0; i > groupList.size(); i++ ) {
835
      	groupnames[i] = groupList.get(i).getGroupName();
836
      	
837
      }
838
      
839
    }
840
  
+    // generate pid/localId pair for object
+    logMetacat.debug("Generating a pid/localId mapping");
+    IdentifierManager im = IdentifierManager.getInstance();
+    String localId = im.generateLocalId(pid.getValue(), 1);
+  
+    try {
+      logMetacat.debug("Case DATA: starting to write to disk.");
+      if (DocumentImpl.getDataFileLockGrant(localId)) {
+  
+        // Save the data file to disk using "localId" as the name
+        try {
+          String datafilepath = PropertyService.getProperty("application.datafilepath");
+  
+          File dataDirectory = new File(datafilepath);
+          dataDirectory.mkdirs();
+  
+          File newFile = writeStreamToFile(dataDirectory, localId, object);
+  
+          // TODO: Check that the file size matches SystemMetadata
+          // long size = newFile.length();
+          // if (size == 0) {
+          //     throw new IOException("Uploaded file is 0 bytes!");
+          // }
+  
+          // Register the file in the database (which generates an exception
+          // if the localId is not acceptable or other untoward things happen
+          try {
+            logMetacat.debug("Registering document...");
+            DocumentImpl.registerDocument(localId, "BIN", localId,
+                    username, groupnames);
+            logMetacat.debug("Registration step completed.");
841
            
+          } catch (SQLException e) {
+            //newFile.delete();
+            logMetacat.debug("SQLE: " + e.getMessage());
+            e.printStackTrace(System.out);
+            throw new ServiceFailure("1190", "Registration failed: " + 
842
            		e.getMessage());
843
            
+          } catch (AccessionNumberException e) {
+            //newFile.delete();
+            logMetacat.debug("ANE: " + e.getMessage());
+            e.printStackTrace(System.out);
+            throw new ServiceFailure("1190", "Registration failed: " + 
844
            	e.getMessage());
845
            
+          } catch (Exception e) {
+            //newFile.delete();
+            logMetacat.debug("Exception: " + e.getMessage());
+            e.printStackTrace(System.out);
+            throw new ServiceFailure("1190", "Registration failed: " + 
846
            	e.getMessage());
+          }
+  
+          logMetacat.debug("Logging the creation event.");
+          EventLog.getInstance().log(metacatUrl, username, localId, "create");
+  
+          // Schedule replication for this data file
+          logMetacat.debug("Scheduling replication.");
+          ForceReplicationHandler frh = new ForceReplicationHandler(
+            localId, "create", false, null);
+  
+        } catch (PropertyNotFoundException e) {
+          throw new ServiceFailure("1190", "Could not lock file for writing:" + 
847
          	e.getMessage());
848
          
+        }
+      }
+      return localId;
+    } catch (Exception e) {
+        // Could not get a lock on the document, so we can not update the file now
+        throw new ServiceFailure("1190", "Failed to lock file: " + e.getMessage());
+    }
849
    
+  }
850
  /**
851
   * Write a stream to a file
852
   * 
853
   * @param dir - the directory to write to
854
   * @param fileName - the file name to write to
855
   * @param data - the object bytes as an input stream
856
   * 
857
   * @return newFile - the new file created
858
   * 
859
   * @throws ServiceFailure
860
   */
861
  private File writeStreamToFile(File dir, String fileName, InputStream data) 
862
    throws ServiceFailure {
863
    
864
    File newFile = new File(dir, fileName);
865
    logMetacat.debug("Filename for write is: " + newFile.getAbsolutePath());
866

  
867
    try {
868
        if (newFile.createNewFile()) {
869
          // write data stream to desired file
870
          OutputStream os = new FileOutputStream(newFile);
871
          long length = IOUtils.copyLarge(data, os);
872
          os.flush();
873
          os.close();
874
        } else {
875
          logMetacat.debug("File creation failed, or file already exists.");
876
          throw new ServiceFailure("1190", "File already exists: " + fileName);
877
        }
878
    } catch (FileNotFoundException e) {
879
      logMetacat.debug("FNF: " + e.getMessage());
880
      throw new ServiceFailure("1190", "File not found: " + fileName + " " 
881
                + e.getMessage());
882
    } catch (IOException e) {
883
      logMetacat.debug("IOE: " + e.getMessage());
884
      throw new ServiceFailure("1190", "File was not written: " + fileName 
885
                + " " + e.getMessage());
886
    }
887

  
888
    return newFile;
889
  }
890
  
891
}

Also available in: Unified diff