package org.fcrepo.server.storage.types;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import org.fcrepo.common.Constants;
import org.fcrepo.server.Context;
import org.fcrepo.server.Module;
import org.fcrepo.server.Server;
import org.fcrepo.server.errors.ModuleInitializationException;
import org.fcrepo.server.errors.ServerInitializationException;
import org.fcrepo.server.errors.StreamIOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Wrapper class for a datastream that has XML metadata.
*
* Allows managed content and inline xml datastreams to be treated generically
* as datastreams having XML metadata content, eg for DC, RELS-EXT and RELS-INT which
* may be stored either inline or as managed content.
*
* @author Stephen Bayliss
* @version $Id$
*/
public class XMLDatastreamProcessor {
private static final Logger logger =
LoggerFactory.getLogger(XMLDatastreamProcessor.class);
protected Datastream m_ds; // the wrapped datastream
private enum DS_TYPE {
INLINE_XML,
MANAGED
}
// when creating new datastreams via this class, these determine the
// type (M or X) of datastream to create. See fedora.fcfg for config
private static String DC_DEFAULT_CONTROLGROUP;
private static String RELS_DEFAULT_CONTROLGROUP;
private static boolean initialized = false;
protected DS_TYPE m_dsType;
@SuppressWarnings("unused")
private XMLDatastreamProcessor() {}
/**
* Construct a new XML datastream processor and the associated datastream
* using the datastream ID to determine the type (M or X) of datastream to
* construct (see fedora.fcfg for configuring the default types)
*
* @param dsId
*/
public XMLDatastreamProcessor(String dsId) {
init();
String controlGroup = null;
if (dsId.equals("DC")) {
controlGroup = DC_DEFAULT_CONTROLGROUP;
} else if (dsId.equals("RELS-EXT") || dsId.equals("RELS-INT")) {
controlGroup = RELS_DEFAULT_CONTROLGROUP;
} else {
// coding error if trying to handle other types of datastream
throw new RuntimeException("XML Datastream Processor only handles DC and RELS datastreams. Datastream ID supplied was " + dsId);
}
if (controlGroup.equals("X")) {
m_ds = new DatastreamXMLMetadata();
m_dsType = DS_TYPE.INLINE_XML;
} else if (controlGroup.equals("M")) {
m_ds = new DatastreamManagedContent();
m_dsType = DS_TYPE.MANAGED;
}
m_ds.DSControlGrp = controlGroup;
m_ds.DatastreamID = dsId;
}
/**
* Construct an XML Datastream Processor by wrapping an existing datastream
*
* @param ds - datastream to wrap
*/
public XMLDatastreamProcessor(Datastream ds) {
if (ds instanceof DatastreamXMLMetadata) {
m_ds = ds;
m_dsType = DS_TYPE.INLINE_XML;
} else if (ds instanceof DatastreamManagedContent) {
m_ds = ds;
m_dsType = DS_TYPE.MANAGED;
} else {
// unhandled type is a coding error
throw new RuntimeException("XML datastreams must be of type Managed or Inline,"
+ " but type was " + ds.getClass().getName());
}
}
private static void init() {
if (!initialized) {
Server server;
// get default types of datastream (M or X) to be used for reserved datastreams
try {
server = Server.getInstance(new File(Constants.FEDORA_HOME),
false);
Module module = server.getModule("org.fcrepo.server.storage.DOManager");
DC_DEFAULT_CONTROLGROUP = module.getParameter("defaultDCControlGroup");
RELS_DEFAULT_CONTROLGROUP = module.getParameter("defaultRELSControlGroup");
} catch (ServerInitializationException e) {
logger.error("Unable to get server", e);
} catch (ModuleInitializationException e) {
logger.error("Unable to get DOManager module", e);
}
if (DC_DEFAULT_CONTROLGROUP == null) {
logger.error("Unable to determine default controlgroup for DC datastreams, using X");
DC_DEFAULT_CONTROLGROUP = "X";
}
if (RELS_DEFAULT_CONTROLGROUP == null) {
logger.error("Unable to determine default controlgroup for RELS datastreams, using X");
RELS_DEFAULT_CONTROLGROUP = "X";
}
initialized = true;
}
}
/**
* Return a new XML Datastream processor wrapping a new datastream. The type (M or X) of the
* new datastream will be the same as the existing one. Use to generate new versions of existing
* datastreams and wrap them in an XML datastream processor.
*
* @throws ServerInitializationException
* @throws ModuleInitializationException
*/
public XMLDatastreamProcessor newVersion() throws ServerInitializationException, ModuleInitializationException {
// create new datastream (version) based on existing datastream control group
Datastream ds;
if (m_dsType == DS_TYPE.INLINE_XML) {
ds = new DatastreamXMLMetadata();
ds.DSControlGrp = "X";
} else if (m_dsType == DS_TYPE.MANAGED) {
ds = new DatastreamManagedContent();
ds.DSControlGrp = "M";
} else {
// unhandled type is a coding error
throw new RuntimeException("XML datastreams must be of type Managed or Inline");
}
return new XMLDatastreamProcessor(ds);
}
/**
* Get the XML content of the datastream wrapped by this class
* @return byte[] contents
*/
public byte [] getXMLContent() {
return getXMLContent(null);
}
public byte[] getXMLContent(Context ctx) {
// could use getContentStream generically instead?
if (m_dsType == DS_TYPE.INLINE_XML)
return ((DatastreamXMLMetadata)m_ds).xmlContent;
else if (m_dsType == DS_TYPE.MANAGED)
try {
if (ctx == null) {
return IOUtils.toByteArray(m_ds.getContentStream());
} else {
return IOUtils.toByteArray(m_ds.getContentStream(ctx));
}
} catch (IOException e) {
throw new RuntimeException("Unable to read managed stream contents", e);
} catch (StreamIOException e) {
throw new RuntimeException("Unable to read managed stream contents", e);
}
else
throw new RuntimeException("XML datastreams must be of type Managed or Inline");
}
/**
* Get the datastream wrapped by this class
* @return Datastream
*/
public Datastream getDatastream() {
return m_ds;
}
/**
* Update the XML content of the datastream wrapped by this class
* @param xmlContent
*/
public void setXMLContent(byte[] xmlContent) {
if (m_dsType == DS_TYPE.INLINE_XML) {
((DatastreamXMLMetadata)m_ds).xmlContent = xmlContent;
((DatastreamXMLMetadata)m_ds).DSSize = (xmlContent != null) ? xmlContent.length : -1;
} else if (m_dsType == DS_TYPE.MANAGED) {
ByteArrayInputStream bais = new ByteArrayInputStream(xmlContent);
MIMETypedStream s = new MIMETypedStream("text/xml", bais, null,xmlContent.length);
try {
((DatastreamManagedContent)m_ds).putContentStream(s);
} catch (StreamIOException e) {
throw new RuntimeException("Unable to update managed datastream contents", e);
}
} else
// coding error if trying to use other datastream type
throw new RuntimeException("XML datastreams must be of type Managed or Inline");
}
/**
* Set the DSMDClass of the datastream wrapped by this class
* @param DSMDClass
*/
public void setDSMDClass(int DSMDClass) {
if (m_dsType == DS_TYPE.INLINE_XML)
((DatastreamXMLMetadata)m_ds).DSMDClass = DSMDClass;
else if (m_dsType == DS_TYPE.MANAGED)
((DatastreamManagedContent)m_ds).DSMDClass = DSMDClass;
else
// coding error if trying to use other datastream type
throw new RuntimeException("XML datastreams must be of type Managed or Inline");
}
/**
* Get the DSMDClass of the datastream wrapped by this class
* @return int
*/
public int getDSMDClass() {
if (m_dsType == DS_TYPE.INLINE_XML)
return ((DatastreamXMLMetadata)m_ds).DSMDClass;
else if (m_dsType == DS_TYPE.MANAGED)
return ((DatastreamManagedContent)m_ds).DSMDClass;
else
// coding error if trying to use other datastream type
throw new RuntimeException("XML datastreams must be of type Managed or Inline");
}
}