Context context, InputStream in, String format, String encoding,
String pid) throws ServerException {
logger.debug("Entered getIngestWriter");
DOWriter w = null;
BasicDigitalObject obj = null;
File tempFile = null;
if (cachedObjectRequired) {
throw new InvalidContextException(
"A DOWriter is unavailable in a cached context.");
} else {
try {
// CURRENT TIME:
// Get the current time to use for created dates on object
// and object components (if they are not already there).
Date nowUTC = Server.getCurrentDate(context);
// TEMP STORAGE:
// write ingest input stream to a temporary file
tempFile = File.createTempFile("fedora-ingest-temp", ".xml");
logger.debug("Creating temporary file for ingest: " +
tempFile.toString());
StreamUtility.pipeStream(in, new FileOutputStream(tempFile),
4096);
// VALIDATION:
// perform initial validation of the ingest submission file
logger.debug("Validation (ingest phase)");
m_validator.validate(tempFile, format, m_ingestValidationLevel,
DOValidator.PHASE_INGEST);
// DESERIALIZE:
// deserialize the ingest input stream into a digital object
// instance
obj = new BasicDigitalObject();
obj.setNew(true);
logger.debug("Deserializing from format: " + format);
m_translator.deserialize(new FileInputStream(tempFile), obj,
format, encoding,
DOTranslationUtility.DESERIALIZE_INSTANCE);
// SET OBJECT PROPERTIES:
logger.debug("Setting object/component states and create dates if unset");
// set object state to "A" (Active) if not already set
if (obj.getState() == null || obj.getState().equals("")) {
obj.setState("A");
}
// set object create date to UTC if not already set
if (obj.getCreateDate() == null ||
obj.getCreateDate().equals("")) {
obj.setCreateDate(nowUTC);
}
// set object last modified date to UTC
obj.setLastModDate(nowUTC);
// SET DATASTREAM PROPERTIES...
Iterator<String> dsIter = obj.datastreamIdIterator();
while (dsIter.hasNext()) {
for (Datastream ds : obj.datastreams(dsIter.next())) {
// Set create date to UTC if not already set
if (ds.DSCreateDT == null || ds.DSCreateDT.equals("")) {
ds.DSCreateDT = nowUTC;
}
// Set state to "A" (Active) if not already set
if (ds.DSState == null || ds.DSState.equals("")) {
ds.DSState = "A";
}
ds.DSChecksumType =
Datastream
.validateChecksumType(ds.DSChecksumType);
}
}
// SET MIMETYPE AND FORMAT_URIS FOR LEGACY OBJECTS' DATASTREAMS
if (FOXML1_0.uri.equals(format) ||
FOXML1_0_LEGACY.equals(format) ||
METS_EXT1_0.uri.equals(format) ||
METS_EXT1_0_LEGACY.equals(format)) {
DigitalObjectUtil.updateLegacyDatastreams(obj);
}
// If the PID was supplied as additional parameter (see REST
// API), make sure it doesn't conflict with the (optional) PID
// of the digital object
if (pid != null && pid.length() > 0 && !pid.equals("new")) {
if (obj.getPid() != null && obj.getPid().length() > 0) {
if (!pid.equals(obj.getPid())) {
throw new GeneralException(
"The PID of the digital object and the PID provided as parameter are different. Digital object: " +
obj.getPid() + " parameter: " + pid);
}
} else {
obj.setPid(pid);
}
}
// PID VALIDATION:
// validate and normalized the provided pid, if any
if (obj.getPid() != null && obj.getPid().length() > 0) {
obj.setPid(Server.getPID(obj.getPid()).toString());
}
// PID GENERATION:
// have the system generate a PID if one was not provided
if (obj.getPid() != null &&
obj.getPid().indexOf(":") != -1 &&
(m_retainPIDs == null || m_retainPIDs.contains(obj
.getPid().split(":")[0]))) {
logger.debug("Stream contained PID with retainable namespace-id; will use PID from stream");
try {
m_pidGenerator.neverGeneratePID(obj.getPid());
} catch (IOException e) {
throw new GeneralException(
"Error calling pidGenerator.neverGeneratePID(): " +
e.getMessage());
}
} else {
if (pid.equals("new")) {
logger.debug("Client wants a new PID");
// yes... so do that, then set it in the obj.
String p = null;
try {
// If the context contains a recovery PID, use that.
// Otherwise, generate a new PID as usual.
if (context instanceof RecoveryContext) {
RecoveryContext rContext =
(RecoveryContext) context;
p =
rContext.getRecoveryValue(Constants.RECOVERY.PID.uri);
}
if (p == null) {
p =
m_pidGenerator.generatePID(
m_pidNamespace).toString();
} else {
logger.debug("Using new PID from recovery context");
m_pidGenerator.neverGeneratePID(p);
}
} catch (Exception e) {
throw new GeneralException("Error generating PID",
e);
}
logger.info("Generated new PID: {}", p);
obj.setPid(p);
} else {
logger.debug("Client wants to use existing PID.");
}
}
logger.debug("New object PID is {}", obj.getPid());
// WRITE LOCK:
// ensure no one else can modify the object now
getWriteLock(obj.getPid());
// CHECK REGISTRY:
// ensure the object doesn't already exist
if (objectExists(obj.getPid())) {
releaseWriteLock(obj.getPid());
throw new ObjectExistsException("The PID '" + obj.getPid() +
"' already exists in the registry; the object can't be re-created.");
}
// GET DIGITAL OBJECT WRITER:
// get an object writer configured with the DEFAULT export
// format
logger.debug("Getting new writer with default export format: {}",
m_defaultExportFormat);
logger.debug("Instantiating a SimpleDOWriter");
w =
new SimpleDOWriter(context, this, m_translator,
m_defaultExportFormat,
m_storageCharacterEncoding, obj);
// DEFAULT DATASTREAMS:
populateDC(context, obj, w, nowUTC);
// DATASTREAM VALIDATION
ValidationUtility.validateReservedDatastreams(w);
// REGISTRY:
// at this point the object is valid, so make a record
// of it in the digital object registry
registerObject(obj);
return w;
} catch (IOException e) {
if (w != null) {
releaseWriteLock(obj.getPid());
}
throw new GeneralException("Error reading/writing temporary "
+ "ingest file", e);
} catch (Exception e) {
if (w != null) {
releaseWriteLock(obj.getPid());
}
if (e instanceof ServerException) {
ServerException se = (ServerException) e;
throw se;