/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.server.access;
import fedora.common.Constants;
import fedora.common.Models;
import fedora.server.Context;
import fedora.server.Module;
import fedora.server.Server;
import fedora.server.access.dissemination.DisseminationService;
import fedora.server.errors.*;
import fedora.server.search.FieldSearchQuery;
import fedora.server.search.FieldSearchResult;
import fedora.server.security.Authorization;
import fedora.server.storage.ContentManagerParams;
import fedora.server.storage.DOManager;
import fedora.server.storage.DOReader;
import fedora.server.storage.ExternalContentManager;
import fedora.server.storage.ServiceDefinitionReader;
import fedora.server.storage.ServiceDeploymentReader;
import fedora.server.storage.types.*;
import fedora.server.utilities.DateUtility;
import org.apache.log4j.Logger;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The Access Module, providing support for the Fedora Access subsystem.
*
* @author Ross Wayland
* @version $Id$
*/
public class DefaultAccess
extends Module
implements Access {
/** Logger for this class. */
private final static Logger LOG = Logger.getLogger(Access.class.getName());
/** Current DOManager of the Fedora server. */
private DOManager m_manager;
/** OAI Provider domain name, for the describe request's identifier info. */
private String m_repositoryDomainName;
/** Dynamic Access Module */
// FIXME is this the right way to associate the dynamic access module???
private DynamicAccessModule m_dynamicAccess;
private ExternalContentManager m_externalContentManager;
private Authorization m_authorizationModule;
/**
* <p>
* Creates and initializes the Access Module. When the server is starting
* up, this is invoked as part of the initialization process.
* </p>
*
* @param moduleParameters
* A pre-loaded Map of name-value pairs comprising the intended
* configuration of this Module.
* @param server
* The <code>Server</code> instance.
* @param role
* The role this module fulfills, a java class name.
* @throws ModuleInitializationException
* If initilization values are invalid or initialization fails for
* some other reason.
*/
public DefaultAccess(Map<String, String> moduleParameters,
Server server,
String role)
throws ModuleInitializationException {
super(moduleParameters, server, role);
}
/**
* <p>
* Initializes the module.
* </p>
*
* @throws ModuleInitializationException
* If the module cannot be initialized.
*/
@Override
public void initModule() throws ModuleInitializationException {
String dsMediation = getParameter("doMediateDatastreams");
if (dsMediation == null) {
throw new ModuleInitializationException("doMediateDatastreams parameter must be specified.",
getRole());
}
}
@Override
public void postInitModule() throws ModuleInitializationException {
// get ref to DOManager
m_manager =
(DOManager) getServer()
.getModule("fedora.server.storage.DOManager");
if (m_manager == null) {
throw new ModuleInitializationException("Can't get a DOManager "
+ "from Server.getModule", getRole());
}
// get ref to DynamicAccess module
m_dynamicAccess =
(DynamicAccessModule) getServer()
.getModule("fedora.server.access.DynamicAccess");
// get ref to ExternalContentManager
m_externalContentManager =
(ExternalContentManager) getServer()
.getModule("fedora.server.storage.ExternalContentManager");
// get ref to OAIProvider, for repositoryDomainName param for oai info
Module oaiProvider = getServer().getModule("fedora.oai.OAIProvider");
if (oaiProvider == null) {
throw new ModuleInitializationException("DefaultAccess module requires that the server "
+ "has an OAIProvider module configured so that it can get the repositoryDomainName parameter.",
getRole());
}
m_repositoryDomainName =
oaiProvider.getParameter("repositoryDomainName");
if (m_repositoryDomainName == null) {
throw new ModuleInitializationException("DefaultAccess module requires that the OAIProvider "
+ "module has the repositoryDomainName parameter specified.",
getRole());
}
m_authorizationModule =
(Authorization) getServer()
.getModule("fedora.server.security.Authorization");
if (m_authorizationModule == null) {
throw new ModuleInitializationException("Can't get an Authorization module (in default access) from Server.getModule",
getRole());
}
}
private static final Hashtable<String, String> accessActionAttributes =
new Hashtable<String, String>();
static {
accessActionAttributes.put("api", "apia");
}
/**
* <p>
* Disseminates the content produced by executing the specified method of
* the associated deployment object of the specified digital object.
* </p>
*
* @param context
* The context of this request.
* @param PID
* The persistent identifier of the digital object.
* @param sDefPID
* The persistent identifier of the Service Definition object.
* @param methodName
* The name of the method to be executed.
* @param userParms
* An array of user-supplied method parameters consisting of
* name/value pairs.
* @param asOfDateTime
* The versioning datetime stamp.
* @return A MIME-typed stream containing the result of the dissemination.
* @throws ServerException
* If any type of error occurred fulfilling the request.
*/
public MIMETypedStream getDissemination(Context context,
String PID,
String sDefPID,
String methodName,
Property[] userParms,
Date asOfDateTime)
throws ServerException {
PID = Server.getPID(PID).toString();
sDefPID = Server.getPID(sDefPID).toString();
long initStartTime = new Date().getTime();
long startTime = new Date().getTime();
long stopTime;
long interval;
ServiceDeploymentReader deploymentReader = null;
DOReader reader =
m_manager.getReader(asOfDateTime == null, context, PID);
String authzAux_objState = reader.GetObjectState();
// DYNAMIC!! If service deployment is defined as dynamic, then
// perform the dissemination via the DynamicAccess module.
if (m_dynamicAccess.isDynamicService(context, PID, sDefPID)) {
m_authorizationModule.enforceGetDissemination(context,
PID,
sDefPID,
methodName,
asOfDateTime,
authzAux_objState,
"A",
"fedora-system:4",
"A",
"A");
MIMETypedStream retVal =
m_dynamicAccess.getDissemination(context,
PID,
sDefPID,
methodName,
userParms,
asOfDateTime);
stopTime = new Date().getTime();
interval = stopTime - startTime;
LOG.debug("Roundtrip DynamicDisseminator: " + interval
+ " milliseconds.");
return retVal;
}
/*
* Find the service deployment that is contractor for a model this
* object has, and deploys the requested service. If object<->model
* mappings are ever stored in the registry, this may be simplified.
*/
String serviceDeploymentPID = null;
for (String cModelURI: reader.getContentModels()){
String cModelPID = cModelURI.substring("info:fedora/".length());
/*
for (RelationshipTuple rel : reader.getRelationships(MODEL.HAS_MODEL,
null)) {
String cModelPID = rel.getObjectPID();
*/
String foundDeploymentPID =
m_manager.lookupDeploymentForCModel(cModelPID, sDefPID);
if (foundDeploymentPID != null) {
if (serviceDeploymentPID != null
&& !foundDeploymentPID.equals(serviceDeploymentPID)) {
throw new DisseminationException("More than one deployment ("
+ foundDeploymentPID
+ ", "
+ serviceDeploymentPID
+ ") found for service "
+ sDefPID
+ " in model "
+ cModelPID);
}
serviceDeploymentPID = foundDeploymentPID;
} else {
LOG.debug("No deployment for (" + cModelPID + ", " + sDefPID
+ ")");
}
}
if (serviceDeploymentPID != null) {
deploymentReader =
m_manager.getServiceDeploymentReader(false,
context,
serviceDeploymentPID);
}
ServiceDefinitionReader sDefReader =
m_manager.getServiceDefinitionReader(asOfDateTime == null,
context,
sDefPID);
String authzAux_sdefState = sDefReader.GetObjectState();
String authzAux_dissState = "unknown";
/*
* if reader is null, it means that no suitable deployments have been
* found. This can happen if (a), the object does not have any models
* that have that service, or (b) the object has a suitable model, but
* no implementation of that service has been deployed. We do a bit of
* checking here to determine which case this represents, as the error
* message could be very useful.
*/
if (deploymentReader == null) {
boolean suitableModelFound = false;
String cModelPID = null;
String message = null;
/*
models: for (RelationshipTuple rel : reader
.getRelationships(MODEL.HAS_MODEL, null)) {
cModelPID = rel.getObjectPID();
*/
models: for (String cm:reader.getContentModels()){
cModelPID = cm.substring(12);
/* Skip over system models */
if (Models.contains("info:fedora/" + cModelPID)) {
continue;
}
/* Open up each model and peek at its sDefs for a match */
for (RelationshipTuple r : m_manager.getReader(false,
context,
cModelPID)
.getRelationships(MODEL.HAS_SERVICE, null)) {
if (sDefPID.equals(r.getObjectPID())) {
suitableModelFound = true;
break models;
}
}
}
if (suitableModelFound) {
message =
"Unable to find deployment for service " + sDefPID
+ " on " + reader.GetObjectPID() + " in model "
+ cModelPID;
} else {
message =
reader.GetObjectPID()
+ " does not have a model with service "
+ sDefPID;
}
throw new DisseminatorNotFoundException(message);
}
stopTime = new Date().getTime();
interval = stopTime - startTime;
LOG.debug("Roundtrip Looping Diss: " + interval + " milliseconds.");
// Check deployment object state
String authzAux_sDepState = deploymentReader.GetObjectState();
String authzAux_sDepPID = deploymentReader.GetObjectPID();
m_authorizationModule.enforceGetDissemination(context,
PID,
sDefPID,
methodName,
asOfDateTime,
authzAux_objState,
authzAux_sdefState,
authzAux_sDepPID,
authzAux_sDepState,
authzAux_dissState);
// Get method parms
Hashtable<String, String> h_userParms = new Hashtable<String, String>();
MIMETypedStream dissemination = null;
MethodParmDef[] defaultMethodParms = null;
startTime = new Date().getTime();
// Put any user-supplied method parameters into hash table
if (userParms != null) {
for (Property element : userParms) {
h_userParms.put(element.name, element.value);
}
}
// Validate user-supplied parameters
validateUserParms(context,
PID,
sDefPID,
deploymentReader,
methodName,
h_userParms,
asOfDateTime);
stopTime = new Date().getTime();
interval = stopTime - startTime;
LOG.debug("Roundtrip Get/Validate User Parms: " + interval
+ " milliseconds.");
startTime = new Date().getTime();
// SDP: GET INFO FROM DEPLOYMENT READER:
// Add any default method parameters to validated user parm list
defaultMethodParms =
deploymentReader
.getServiceMethodParms(methodName, asOfDateTime);
for (int i = 0; i < defaultMethodParms.length; i++) {
if (!defaultMethodParms[i].parmType
.equals(MethodParmDef.DATASTREAM_INPUT)) {
if (!h_userParms.containsKey(defaultMethodParms[i].parmName)) {
LOG.debug("addedDefaultName: "
+ defaultMethodParms[i].parmName);
String pdv = defaultMethodParms[i].parmDefaultValue;
try {
// here we make sure the PID is decoded so that encoding
// later won't doubly-encode it
if (pdv.equalsIgnoreCase("$pid")) {
pdv = URLDecoder.decode(PID, "UTF-8");
} else if (pdv.equalsIgnoreCase("$objuri")) {
pdv =
"info:fedora/"
+ URLDecoder.decode(PID, "UTF-8");
}
} catch (UnsupportedEncodingException uee) {
}
LOG.debug("addedDefaultValue: " + pdv);
h_userParms.put(defaultMethodParms[i].parmName, pdv);
}
}
}
stopTime = new Date().getTime();
interval = stopTime - startTime;
LOG.debug("Roundtrip Get Deployment Parms: " + interval
+ " milliseconds.");
startTime = new Date().getTime();
DisseminationBindingInfo[] dissBindInfo;
dissBindInfo =
getDisseminationBindingInfo(context,
reader,
deploymentReader,
methodName,
asOfDateTime);
// Assemble and execute the dissemination request from the binding info.
DisseminationService dissService = new DisseminationService();
dissemination =
dissService.assembleDissemination(context,
PID,
h_userParms,
dissBindInfo,
authzAux_sDepPID,
deploymentReader,
methodName);
stopTime = new Date().getTime();
interval = stopTime - startTime;
LOG.debug("Roundtrip Assemble Dissemination: " + interval
+ " milliseconds.");
stopTime = new Date().getTime();
interval = stopTime - initStartTime;
LOG.debug("Roundtrip GetDissemination: " + interval + " milliseconds.");
return dissemination;
}
private DisseminationBindingInfo[] getDisseminationBindingInfo(Context context,
DOReader dObj,
ServiceDeploymentReader bmReader,
String methodName,
Date versDateTime)
throws MethodNotFoundException, ServerException {
// The sDep reader provides information about the service and params.
MethodParmDef[] methodParms =
bmReader.getServiceMethodParms(methodName, versDateTime);
// Find the operation bindings for the method in question
MethodDefOperationBind[] opBindings =
bmReader.getServiceMethodBindings(versDateTime);
String addressLocation = null;
String operationLocation = null;
String protocolType = null;
boolean foundMethod = false;
for (MethodDefOperationBind element : opBindings) {
if (element.methodName.equals(methodName)) {
foundMethod = true;
addressLocation = element.serviceBindingAddress;
operationLocation = element.operationLocation;
protocolType = element.protocolType;
}
}
if (!foundMethod) {
throw new MethodNotFoundException("Method " + methodName
+ " was not found in " + bmReader.GetObjectPID()
+ "'s operation " + " binding.");
}
DeploymentDSBindSpec dsBindSpec =
bmReader.getServiceDSInputSpec(versDateTime);
DeploymentDSBindRule[] dsBindRules =
dsBindSpec.dsBindRules == null ? new DeploymentDSBindRule[0]
: dsBindSpec.dsBindRules;
// Results will be returned in this list, one item per *existing*
// datastream. If a datastream mentioned in the dsBindRules is not
// present in the object, it will not be present in this list.
// If the datastream is *really* required in order to invoke the
// dissemination method in question, rest assured it will fail later.
List<DisseminationBindingInfo> bindingInfoList =
new ArrayList<DisseminationBindingInfo>();
for (int i = 0; i < dsBindRules.length; i++) {
DeploymentDSBindRule dsBindRule = dsBindRules[i];
String dsPid =
dsBindRule.pid == null ? dObj.GetObjectPID()
: dsBindRule.pid;
String dsId = dsBindRule.bindingKeyName;
DOReader reader = m_manager.getReader(false, context, dsPid);
Datastream ds = reader.GetDatastream(dsId, versDateTime);
if (ds != null) {
DisseminationBindingInfo bindingInfo =
new DisseminationBindingInfo();
bindingInfo.DSBindKey = dsId;
bindingInfo.dsLocation = ds.DSLocation;
bindingInfo.dsControlGroupType = ds.DSControlGrp;
bindingInfo.dsID = ds.DatastreamID;
bindingInfo.dsVersionID = ds.DSVersionID;
bindingInfo.dsState = ds.DSState;
bindingInfo.dsCreateDT = ds.DSCreateDT;
// these will be the same for all elements of the array
bindingInfo.methodParms = methodParms;
bindingInfo.AddressLocation = addressLocation;
bindingInfo.OperationLocation = operationLocation;
bindingInfo.ProtocolType = protocolType;
bindingInfoList.add(bindingInfo);
}
}
return bindingInfoList.toArray(new DisseminationBindingInfo[0]);
}
public ObjectMethodsDef[] listMethods(Context context,
String PID,
Date asOfDateTime)
throws ServerException {
long startTime = new Date().getTime();
PID = Server.getPID(PID).toString();
m_authorizationModule.enforceListMethods(context, PID, asOfDateTime);
DOReader reader =
m_manager.getReader(Server.USE_DEFINITIVE_STORE, context, PID);
ObjectMethodsDef[] methodDefs = reader.listMethods(asOfDateTime);
long stopTime = new Date().getTime();
long interval = stopTime - startTime;
LOG.debug("Roundtrip listMethods: " + interval + " milliseconds.");
// DYNAMIC!! Grab any dynamic method definitions and merge them with
// the statically bound method definitions
ObjectMethodsDef[] dynamicMethodDefs =
//m_dynamicAccess.getObjectMethods(context, PID, asOfDateTime);
m_dynamicAccess.listMethods(context, PID, asOfDateTime);
ArrayList<ObjectMethodsDef> methodList =
new ArrayList<ObjectMethodsDef>();
for (ObjectMethodsDef element : methodDefs) {
methodList.add(element);
}
for (ObjectMethodsDef element : dynamicMethodDefs) {
methodList.add(element);
}
return methodList.toArray(new ObjectMethodsDef[0]);
}
public DatastreamDef[] listDatastreams(Context context,
String PID,
Date asOfDateTime)
throws ServerException {
long startTime = new Date().getTime();
PID = Server.getPID(PID).toString();
m_authorizationModule
.enforceListDatastreams(context, PID, asOfDateTime);
DOReader reader =
m_manager.getReader(Server.USE_DEFINITIVE_STORE, context, PID);
Datastream[] datastreams = reader.GetDatastreams(asOfDateTime, null);
DatastreamDef[] dsDefs = new DatastreamDef[datastreams.length];
for (int i = 0; i < datastreams.length; i++) {
dsDefs[i] =
new DatastreamDef(datastreams[i].DatastreamID,
datastreams[i].DSLabel,
datastreams[i].DSMIME);
}
long stopTime = new Date().getTime();
long interval = stopTime - startTime;
LOG.debug("Roundtrip listDatastreams: " + interval + " milliseconds.");
return dsDefs;
}
public ObjectProfile getObjectProfile(Context context,
String PID,
Date asOfDateTime)
throws ServerException {
PID = Server.getPID(PID).toString();
m_authorizationModule.enforceGetObjectProfile(context,
PID,
asOfDateTime);
DOReader reader =
m_manager.getReader(asOfDateTime == null, context, PID);
Date versDateTime = asOfDateTime;
ObjectProfile profile = new ObjectProfile();
profile.PID = reader.GetObjectPID();
profile.objectLabel = reader.GetObjectLabel();
profile.objectOwnerId = reader.getOwnerId();
profile.objectModels = new HashSet<String>();
profile.objectCreateDate = reader.getCreateDate();
profile.objectLastModDate = reader.getLastModDate();
profile.objectState = reader.GetObjectState();
profile.objectModels.addAll(reader.getContentModels());
/*
for (RelationshipTuple rel : reader
.getRelationships(Constants.MODEL.HAS_MODEL, null)) {
profile.objectModels.add(rel.object);
}
*/
String reposBaseURL =
getReposBaseURL(context
.getEnvironmentValue(Constants.HTTP_REQUEST.SECURITY.uri)
.equals(Constants.HTTP_REQUEST.SECURE.uri) ? "https"
: "http",
context
.getEnvironmentValue(Constants.HTTP_REQUEST.SERVER_PORT.uri));
profile.dissIndexViewURL =
getDissIndexViewURL(reposBaseURL,
context
.getEnvironmentValue(Constants.FEDORA_APP_CONTEXT_NAME),
reader.GetObjectPID(),
versDateTime);
profile.itemIndexViewURL =
getItemIndexViewURL(reposBaseURL,
context
.getEnvironmentValue(Constants.FEDORA_APP_CONTEXT_NAME),
reader.GetObjectPID(),
versDateTime);
return profile;
}
/**
* <p>
* Lists the specified fields of each object matching the given criteria.
* </p>
*
* @param context
* the context of this request
* @param resultFields
* the names of the fields to return
* @param maxResults
* the maximum number of results to return at a time
* @param query
* the query
* @return the results of te field search
* @throws ServerException
* If any type of error occurred fulfilling the request.
*/
public FieldSearchResult findObjects(Context context,
String[] resultFields,
int maxResults,
FieldSearchQuery query)
throws ServerException {
m_authorizationModule.enforceFindObjects(context);
return m_manager.findObjects(context, resultFields, maxResults, query);
}
/**
* <p>
* Resumes an in-progress listing of object fields.
* </p>
*
* @param context
* the context of this request
* @param sessionToken
* the token of the session in which the remaining results can be
* obtained
* @return the next set of results from the initial field search
* @throws ServerException
* If any type of error occurred fulfilling the request.
*/
public FieldSearchResult resumeFindObjects(Context context,
String sessionToken)
throws ServerException {
m_authorizationModule.enforceFindObjects(context);
return m_manager.resumeFindObjects(context, sessionToken);
}
/**
* <p>
* Gets information that describes the repository.
* </p>
*
* @param context
* the context of this request
* @return information that describes the repository.
* @throws ServerException
* If any type of error occurred fulfilling the request.
*/
public RepositoryInfo describeRepository(Context context)
throws ServerException {
m_authorizationModule.enforceDescribeRepository(context);
RepositoryInfo repositoryInfo = new RepositoryInfo();
repositoryInfo.repositoryName =
getServer().getParameter("repositoryName");
String reposBaseURL =
getReposBaseURL(context
.getEnvironmentValue(Constants.HTTP_REQUEST.SECURITY.uri)
.equals(Constants.HTTP_REQUEST.SECURE.uri) ? "https"
: "http",
context
.getEnvironmentValue(Constants.HTTP_REQUEST.SERVER_PORT.uri));
repositoryInfo.repositoryBaseURL =
reposBaseURL + "/" + context.getEnvironmentValue(Constants.FEDORA_APP_CONTEXT_NAME);
repositoryInfo.repositoryVersion = Server.VERSION;
Module domgr = getServer().getModule("fedora.server.storage.DOManager");
repositoryInfo.repositoryPIDNamespace =
domgr.getParameter("pidNamespace");
repositoryInfo.defaultExportFormat =
domgr.getParameter("defaultExportFormat");
repositoryInfo.OAINamespace = m_repositoryDomainName;
repositoryInfo.adminEmailList = getAdminEmails();
repositoryInfo.samplePID =
repositoryInfo.repositoryPIDNamespace + ":100";
repositoryInfo.sampleOAIIdentifer =
"oai:" + repositoryInfo.OAINamespace + ":"
+ repositoryInfo.samplePID;
repositoryInfo.sampleSearchURL =
repositoryInfo.repositoryBaseURL + "/search";
repositoryInfo.sampleAccessURL =
repositoryInfo.repositoryBaseURL + "/get/" + "demo:5";
repositoryInfo.sampleOAIURL =
repositoryInfo.repositoryBaseURL + "/oai?verb=Identify";
repositoryInfo.retainPIDs = getRetainPIDs();
return repositoryInfo;
}
/**
* <p>
* Gets the change history of an object by returning a list of timestamps
* that correspond to modification dates of components. This currently
* includes changes to datastreams and disseminators.
* </p>
*
* @param context
* The context of this request.
* @param PID
* The persistent identifier of the digitla object.
* @return An Array containing the list of timestamps indicating when
* changes were made to the object.
* @throws ServerException
* If any type of error occurred fulfilling the request.
*/
public String[] getObjectHistory(Context context, String PID)
throws ServerException {
PID = Server.getPID(PID).toString();
m_authorizationModule.enforceGetObjectHistory(context, PID);
DOReader reader =
m_manager.getReader(Server.USE_DEFINITIVE_STORE, context, PID);
return reader.getObjectHistory(PID);
}
private String[] getAdminEmails() {
String emailsCSV =
convertToCSV(getServer().getParameter("adminEmailList"));
Vector<Object> emails = new Vector<Object>();
StringTokenizer st = new StringTokenizer(emailsCSV, ",");
while (st.hasMoreElements()) {
emails.add(st.nextElement());
}
return emails.toArray(new String[0]);
}
private String[] getRetainPIDs() {
String retainPIDsCSV =
convertToCSV(getServer()
.getModule("fedora.server.storage.DOManager")
.getParameter("retainPIDs"));
Vector<Object> retainPIDs = new Vector<Object>();
StringTokenizer st = new StringTokenizer(retainPIDsCSV, ",");
while (st.hasMoreElements()) {
retainPIDs.add(st.nextElement());
}
return retainPIDs.toArray(new String[0]);
}
private String convertToCSV(String list) {
// make sure values in the list are comma delimited
if (list == null) {
return "*";
}
String original = list.trim();
Pattern spaces = Pattern.compile(" ++");
Matcher m = spaces.matcher(original);
String interim = m.replaceAll(",");
Pattern multcommas = Pattern.compile(",++");
Matcher m2 = multcommas.matcher(interim);
String csv = m2.replaceAll(",");
return csv;
}
/**
* <p>
* Validates user-supplied method parameters against values in the
* corresponding Service Definition object. The method will validate for:
* </p>
* <ol>
* <li>Valid name - each name must match a valid method parameter name</li>
* <li>DefaultValue - any specified parameters with valid default values
* will have the default value substituted if the user-supplied value is
* null</li>
* <li>Required name - each required method parameter name must be present
* </ol>
*
* @param context
* The context of this request.
* @param PID
* The persistent identifier of the digital object.
* @param sDefPID
* The persistent identifier of the Service Definition object.
* @param methodName
* The name of the method.
* @param h_userParms
* A hashtable of user-supplied method parameter name/value pairs.
* @param versDateTime
* The version datetime stamp of the digital object.
* @throws ServerException
* If any type of error occurred fulfilling the request.
*/
private void validateUserParms(Context context,
String PID,
String sDefPID,
ServiceDeploymentReader sdepreader,
String methodName,
Hashtable<String, String> h_userParms,
Date versDateTime) throws ServerException {
PID = Server.getPID(PID).toString();
sDefPID = Server.getPID(sDefPID).toString();
MethodParmDef[] methodParms = null;
MethodParmDef methodParm = null;
StringBuffer sb = new StringBuffer();
Hashtable<String, MethodParmDef> h_validParms =
new Hashtable<String, MethodParmDef>();
boolean isValid = true;
if (sdepreader != null) // this code will be used for the CMDA example
{
MethodDef[] methods = sdepreader.getServiceMethods(versDateTime);
// Filter out parms that are internal to the mechanism and not part
// of the abstract method definition. We just want user parms.
for (MethodDef element : methods) {
if (element.methodName.equalsIgnoreCase(methodName)) {
ArrayList<MethodParmDef> filteredParms =
new ArrayList<MethodParmDef>();
MethodParmDef[] parms = element.methodParms;
for (MethodParmDef element2 : parms) {
if (element2.parmType
.equalsIgnoreCase(MethodParmDef.USER_INPUT)) {
filteredParms.add(element2);
}
}
methodParms = filteredParms.toArray(new MethodParmDef[0]);
}
}
} else {
String message =
"[DefaultAccess] Old-style disseminators are no longer supported ";
throw new DisseminatorNotFoundException(message);
// reader = m_manager.getReader(Server.GLOBAL_CHOICE, context, PID);
// methodParms = reader.getObjectMethodParms(sDefPID, methodName, versDateTime);
}
// Put valid method parameters and their attributes into hashtable
if (methodParms != null) {
for (int i = 0; i < methodParms.length; i++) {
methodParm = methodParms[i];
h_validParms.put(methodParm.parmName, methodParm);
if (LOG.isDebugEnabled()) {
LOG.debug("methodParms[" + i + "]: "
+ methodParms[i].parmName + "\nlabel: "
+ methodParms[i].parmLabel + "\ndefault: "
+ methodParms[i].parmDefaultValue + "\nrequired: "
+ methodParms[i].parmRequired + "\ntype: "
+ methodParms[i].parmType);
for (String element : methodParms[i].parmDomainValues) {
LOG.debug("domainValue: " + element);
}
}
}
}
if (!h_validParms.isEmpty()) {
// Iterate over valid parmameters to check for any missing required parms.
Enumeration<String> e = h_validParms.keys();
while (e.hasMoreElements()) {
String validName = e.nextElement();
MethodParmDef mp = h_validParms.get(validName);
if (mp.parmRequired && h_userParms.get(validName) == null) {
// This is a fatal error. A required method parameter does not
// appear in the list of user supplied parameters.
sb.append("The required parameter \"" + validName
+ "\" was not found in the "
+ "user-supplied parameter list.");
throw new InvalidUserParmException("[Invalid User Parameters] "
+ sb.toString());
}
}
// Iterate over each user supplied parameter name
Enumeration<String> parmNames = h_userParms.keys();
while (parmNames.hasMoreElements()) {
String parmName = parmNames.nextElement();
methodParm = h_validParms.get(parmName);
if (methodParm != null && methodParm.parmName != null) {
// Method has one or more parameters defined
// Check for default value if user-supplied value is null or empty
String value = h_userParms.get(methodParm.parmName);
if (value == null || value.equalsIgnoreCase("")) {
// Value of user-supplied parameter is null or empty
if (methodParm.parmDefaultValue != null) {
// Default value is specified for this parameter.
// Substitute default value.
h_userParms.put(methodParm.parmName,
methodParm.parmDefaultValue);
} else {
// This is a non-fatal error. There is no default specified
// for this parameter and the user has supplied no value for
// the parameter. The value of the empty string will be used
// as the value of the parameter.
LOG.warn("The method parameter \""
+ methodParm.parmName
+ "\" has no default value and no "
+ "value was specified by the user. "
+ "The value of the empty string has "
+ "been assigned to this parameter.");
}
} else {
// Value of user-supplied parameter contains a value.
// Validate the supplied value against the parmDomainValues list.
String[] parmDomainValues = methodParm.parmDomainValues;
if (parmDomainValues.length > 0) {
if (!parmDomainValues[0].equalsIgnoreCase("null")) {
boolean isValidValue = false;
String userValue =
h_userParms.get(methodParm.parmName);
for (String element : parmDomainValues) {
if (userValue.equalsIgnoreCase(element)
|| element.equalsIgnoreCase("null")) {
isValidValue = true;
}
}
if (!isValidValue) {
for (int i = 0; i < parmDomainValues.length; i++) {
if (i == parmDomainValues.length - 1) {
sb.append(parmDomainValues[i]);
} else {
sb.append(parmDomainValues[i]
+ ", ");
}
}
sb
.append("The method parameter \""
+ methodParm.parmName
+ "\" with a value of \""
+ h_userParms
.get(methodParm.parmName)
+ "\" is not allowed for the method \""
+ methodName
+ "\". Allowed values for this "
+ "method include \""
+ sb.toString() + "\".");
isValid = false;
}
}
}
}
} else {
// This is a fatal error. A user-supplied parameter name does
// not match any valid parameter names for this method.
sb.append("The method parameter \"" + parmName
+ "\" is not valid for the method \"" + methodName
+ "\".");
isValid = false;
}
}
} else {
// There are no method parameters define for this method.
if (!h_userParms.isEmpty()) {
// This is an error. There are no method parameters defined for
// this method and user parameters are specified in the
// dissemination request.
Enumeration<String> e = h_userParms.keys();
while (e.hasMoreElements()) {
sb.append("The method parameter \"" + e.nextElement()
+ "\" is not valid for the method \"" + methodName
+ "\"." + "The method \"" + methodName
+ "\" defines no method parameters.");
}
throw new InvalidUserParmException("[Invalid User Parameters] "
+ sb.toString());
}
}
if (!isValid) {
throw new InvalidUserParmException("[Invalid User Parameter] "
+ sb.toString());
}
return;
}
private String getDissIndexViewURL(String reposBaseURL,
String fedoraContext,
String PID,
Date versDateTime) {
String dissIndexURL = null;
if (versDateTime == null) {
dissIndexURL =
reposBaseURL + "/" + fedoraContext + "/get/" + PID
+ "/fedora-system:3/viewMethodIndex";
} else {
dissIndexURL =
reposBaseURL + "/" + fedoraContext + "/get/" + PID
+ "/fedora-system:3/viewMethodIndex/"
+ DateUtility.convertDateToString(versDateTime);
}
return dissIndexURL;
}
// FIXIT!! Consider implications of hard-coding the default dissemination
// aspects of the URL (e.g. fedora-system3 as the PID and viewItemIndex.
private String getItemIndexViewURL(String reposBaseURL,
String fedoraContext,
String PID,
Date versDateTime) {
String itemIndexURL = null;
if (versDateTime == null) {
itemIndexURL =
reposBaseURL + "/" + fedoraContext + "/get/" + PID
+ "/fedora-system:3/viewItemIndex";
} else {
itemIndexURL =
reposBaseURL + "/" + fedoraContext + "/get/" + PID
+ "/fedora-system:3/viewItemIndex/"
+ DateUtility.convertDateToString(versDateTime);
}
return itemIndexURL;
}
private String getReposBaseURL(String protocol, String port) {
String reposBaseURL = null;
String fedoraServerHost = getServer().getParameter("fedoraServerHost");
if (fedoraServerHost == null || fedoraServerHost.equals("")) {
LOG.warn("Configuration parameter fedoraServerHost is empty.");
try {
InetAddress hostIP = InetAddress.getLocalHost();
fedoraServerHost = hostIP.getHostName();
} catch (UnknownHostException e) {
LOG.error("Unable to resolve host of Fedora server", e);
fedoraServerHost = "localhost";
}
}
reposBaseURL = protocol + "://" + fedoraServerHost + ":" + port;
return reposBaseURL;
}
public MIMETypedStream getDatastreamDissemination(Context context,
String PID,
String dsID,
Date asOfDateTime)
throws ServerException {
PID = Server.getPID(PID).toString();
m_authorizationModule.enforceGetDatastreamDissemination(context,
PID,
dsID,
asOfDateTime);
MIMETypedStream mimeTypedStream = null;
long startTime = new Date().getTime();
DOReader reader =
m_manager.getReader(Server.USE_DEFINITIVE_STORE, context, PID);
Datastream ds = reader.GetDatastream(dsID, asOfDateTime);
if (ds == null) {
String message =
"[DefaulAccess] No datastream could be returned. "
+ "Either there is no datastream for the digital "
+ "object \""
+ PID
+ "\" with datastream ID of \""
+ dsID
+ " \" OR there are no datastreams that match the specified "
+ "date/time value of \""
+ DateUtility.convertDateToString(asOfDateTime)
+ " \" .";
throw new DatastreamNotFoundException(message);
}
if (ds.DSControlGrp.equalsIgnoreCase("E")) {
DatastreamReferencedContent drc =
(DatastreamReferencedContent) reader
.GetDatastream(dsID, asOfDateTime);
ContentManagerParams params = new ContentManagerParams(drc.DSLocation,
drc.DSMIME, null, null);
params.setContext(context);
mimeTypedStream = m_externalContentManager.getExternalContent(params);
} else if (ds.DSControlGrp.equalsIgnoreCase("M")) {
DatastreamManagedContent dmc =
(DatastreamManagedContent) reader
.GetDatastream(dsID, asOfDateTime);
mimeTypedStream =
new MIMETypedStream(ds.DSMIME, dmc.getContentStream(), null);
} else if (ds.DSControlGrp.equalsIgnoreCase("X")) {
DatastreamXMLMetadata dxm =
(DatastreamXMLMetadata) reader.GetDatastream(dsID,
asOfDateTime);
mimeTypedStream =
new MIMETypedStream(ds.DSMIME, dxm.getContentStream(), null);
} else if (ds.DSControlGrp.equalsIgnoreCase("R")) {
DatastreamReferencedContent drc =
(DatastreamReferencedContent) reader
.GetDatastream(dsID, asOfDateTime);
// The dsControlGroupType of Redirect("R") is a special control type
// used primarily for streaming media. Datastreams of this type are
// not mediated (proxied by Fedora) and their physical dsLocation is
// simply redirected back to the client. Therefore, the contents
// of the MIMETypedStream returned for dissemination requests will
// contain the raw URL of the dsLocation and will be assigned a
// special fedora-specific MIME type to identify the stream as
// a MIMETypedStream whose contents contain a URL to which the client
// should be redirected.
try {
InputStream inStream =
new ByteArrayInputStream(drc.DSLocation
.getBytes("UTF-8"));
mimeTypedStream =
new MIMETypedStream("application/fedora-redirect",
inStream,
null);
} catch (UnsupportedEncodingException uee) {
String message =
"[DefaultAccess] An error has occurred. "
+ "The error was a \""
+ uee.getClass().getName() + "\" . The "
+ "Reason was \"" + uee.getMessage()
+ "\" . String value: " + drc.DSLocation
+ " . ";
LOG.error(message);
throw new GeneralException(message);
}
}
long stopTime = new Date().getTime();
long interval = stopTime - startTime;
LOG.debug("Roundtrip getDatastreamDissemination: " + interval
+ " milliseconds.");
return mimeTypedStream;
}
}