All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.fedora.server.access.dissemination.DisseminationService Maven / Gradle / Ivy

Go to download

The Fedora Client is a Java Library that allows API access to a Fedora Repository. The client is typically one part of a full Fedora installation.

The newest version!
/*
 * -----------------------------------------------------------------------------
 *
 * 

License and Copyright: The contents of this file are subject to the * Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of * the License at * http://www.fedora-commons.org/licenses.

* *

Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License.

* *

The entire file consists of original code.

*

Copyright © 2008 Fedora Commons, Inc.
*

Copyright © 2002-2007 The Rector and Visitors of the University of * Virginia and Cornell University
* All rights reserved.

* * ----------------------------------------------------------------------------- */ package fedora.server.access.dissemination; import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.sql.Timestamp; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.httpclient.Header; import org.apache.log4j.Logger; import fedora.common.Constants; import fedora.common.http.WebClient; import fedora.common.http.HttpInputStream; import fedora.server.Context; import fedora.server.Server; import fedora.server.errors.DisseminationException; import fedora.server.errors.DisseminationBindingInfoNotFoundException; import fedora.server.errors.GeneralException; import fedora.server.errors.HttpServiceNotFoundException; import fedora.server.errors.InitializationException; import fedora.server.errors.ServerException; import fedora.server.errors.ServerInitializationException; import fedora.server.security.Authorization; import fedora.server.security.BackendPolicies; import fedora.server.security.BackendSecurity; import fedora.server.security.BackendSecuritySpec; import fedora.server.storage.DOManager; import fedora.server.storage.DOReader; import fedora.server.storage.types.Datastream; import fedora.server.storage.types.DatastreamMediation; import fedora.server.storage.types.DisseminationBindingInfo; import fedora.server.storage.types.MIMETypedStream; import fedora.server.storage.types.MethodParmDef; import fedora.server.storage.types.Property; import fedora.server.utilities.DateUtility; import fedora.server.utilities.ServerUtility; /** *

Title: DisseminationService.java

*

Description: A service for executing a dissemination given its * binding information.

* * @author [email protected] * @version $Id: DisseminationService.java 6244 2007-10-31 18:54:34Z rwayland3 $ */ public class DisseminationService { /** Logger for this class. */ private static final Logger LOG = Logger.getLogger( DisseminationService.class.getName()); /** The Fedora Server instance */ private static Server s_server; /** An instance of DO manager */ private static DOManager m_manager; /** Signifies the special type of address location known as LOCAL. * An address location of LOCAL implies that no remote host name is * required for the address location and that the contents of the * operation location are sufficient to execute the associated mechanism. */ private static final String LOCAL_ADDRESS_LOCATION = "LOCAL"; /** The expiration limit in minutes for removing entries from the database. */ private static int datastreamExpirationLimit = 0; /** An incremental counter used to insure uniqueness of tempIDs used for * datastream mediation. */ private static int counter = 0; /** Datastream Mediation control flag. */ private static boolean doDatastreamMediation; /** Configured Fedora server host */ private static String fedoraServerHost = null; /** Configured Fedora server port */ private static String fedoraServerPort = null; /** Configured Fedora redirect port */ private static String fedoraServerRedirectPort = null; private static String fedoraHome = null; private static BackendSecuritySpec m_beSS = null; private static BackendSecurity m_beSecurity; private static WebClient s_http; /** Make sure we have a server instance for error logging purposes. */ static { try { fedoraHome = Constants.FEDORA_HOME; if (fedoraHome == null) { throw new ServerInitializationException( "[DisseminationService] Server failed to initialize: " + "FEDORA_HOME is undefined"); } else { s_server = Server.getInstance(new File(fedoraHome), false); fedoraServerHost = s_server.getParameter("fedoraServerHost"); fedoraServerPort = s_server.getParameter("fedoraServerPort"); fedoraServerRedirectPort = s_server.getParameter("fedoraRedirectPort"); m_manager = (DOManager) s_server.getModule("fedora.server.storage.DOManager"); m_beSecurity = (BackendSecurity) s_server.getModule("fedora.server.security.BackendSecurity"); m_beSS = m_beSecurity.getBackendSecuritySpec(); String expireLimit = s_server.getParameter("datastreamExpirationLimit"); if (expireLimit == null || expireLimit.equalsIgnoreCase("")) { LOG.info("datastreamExpirationLimit unspecified; defaulting to " + "300 seconds"); datastreamExpirationLimit = 300; } else { datastreamExpirationLimit = new Integer(expireLimit).intValue(); LOG.info("datastreamExpirationLimit=" + datastreamExpirationLimit); } String dsMediation = s_server.getModule("fedora.server.access.Access").getParameter("doMediateDatastreams"); if (dsMediation == null || dsMediation.equalsIgnoreCase("")) { LOG.info("doMediateDatastreams unspecified; defaulting to false"); } else { doDatastreamMediation = new Boolean(dsMediation).booleanValue(); } s_http = new WebClient(); s_http.USER_AGENT = "Fedora"; } } catch (InitializationException ie) { LOG.error("Initialization error", ie); } } /** The hashtable containing information required for datastream mediation. */ protected static Hashtable dsRegistry = new Hashtable(1000); protected static Hashtable beSecurityHash = new Hashtable(); /** *

Constructs an instance of DisseminationService. Initializes two class * variables that contain the IP address and port number of the Fedora server. * The port number is obtained from the Fedora server config file and the IP * address of the server is obtained dynamically. These variables are needed * to perform the datastream proxy service for datastream requests.

*/ public DisseminationService() { } /* public void checkState(Context context, String state, String dsID, String PID) throws ServerException { // Check Object State if ( state.equalsIgnoreCase("D") && ( context.get("canUseDeletedObject")==null || (!context.get("canUseDeletedObject").equals("true")) ) ) { throw new GeneralException("The requested dissemination for data object \""+PID+"\" is no " + "longer available. One of its datastreams (dsID=\""+dsID+"\") has been flagged for DELETION " + "by the repository administrator. "); } else if ( state.equalsIgnoreCase("I") && ( context.get("canUseInactiveObject")==null || (!context.get("canUseInactiveObject").equals("true")) ) ) { throw new GeneralException("The requested dissemination for data object \""+PID+"\" is no " + "longer available. One of its datastreams (dsID=\""+dsID+"\") has been flagged as INACTIVE " + "by the repository administrator. "); } } */ /** *

Assembles a dissemination given an instance of * DisseminationBindingInfo which has the dissemination-related * information from the digital object and its associated Behavior * Mechanism object.

* * @param context The current context. * @param PID The persistent identifier of the digital object. * @param h_userParms A hashtable of user-supplied method parameters. * @param dissBindInfoArray The associated dissemination binding information. * @return A MIME-typed stream containing the result of the dissemination. * @throws ServerException If unable to assemble the dissemination for any * reason. */ public MIMETypedStream assembleDissemination(Context context, String PID, Hashtable h_userParms, DisseminationBindingInfo[] dissBindInfoArray, String bMechPid, String methodName) throws ServerException { LOG.debug("Started assembling dissemination"); String dissURL = null; String protocolType = null; DisseminationBindingInfo dissBindInfo = null; MIMETypedStream dissemination = null; long initStartTime = new Date().getTime(); boolean isRedirect = false; if (LOG.isDebugEnabled()) { printBindingInfo(dissBindInfoArray); } if (dissBindInfoArray != null && dissBindInfoArray.length > 0) { String replaceString = null; int numElements = dissBindInfoArray.length; // Get row(s) of binding info and perform string substitution // on DSBindingKey and method parameter values in WSDL // Note: In case where more than one datastream matches the // DSBindingKey or there are multiple DSBindingKeys for the // method, multiple rows will be present; otherwise there is only // a single row. for (int i=0; i1) { StringBuffer replaced=new StringBuffer(); replaced.append(parts[0]); for (int x=1; x= 0) { dissURL = dissURL.replaceFirst(":"+fedoraServerPort+"/", ":"+fedoraServerRedirectPort+"/"); } } else { if (dissURL.startsWith("https:")) { dissURL = dissURL.replaceFirst("https:", "http:"); } if (dissURL.indexOf(":"+fedoraServerRedirectPort+"/") >= 0) { dissURL = dissURL.replaceFirst(":"+fedoraServerRedirectPort+"/", ":"+fedoraServerPort+"/"); } } if (beServiceCallBasicAuth) { if (dissURL.indexOf("getDS?") >= 0) { dissURL = dissURL.replaceFirst("getDS\\?", "getDSAuthenticated\\?"); } } else { if (dissURL.indexOf("getDSAuthenticated?") >= 0) { dissURL = dissURL.replaceFirst("getDSAuthenticated\\?", "getDS\\?"); } } } */ if (LOG.isDebugEnabled()) { LOG.debug("******************getDisseminationContent beServiceRole: "+beServiceRole); LOG.debug("******************getDisseminationContent beServiceCallBasicAuth: "+beServiceCallBasicAuth); LOG.debug("******************getDisseminationContent beServiceCallSSL: "+beServiceCallSSL); LOG.debug("******************getDisseminationContent beServiceCallUsername: "+beServiceCallUsername); LOG.debug("******************getDisseminationContent beServiceCallPassword: "+beServiceCallPassword); LOG.debug("******************getDisseminationContent dissURL: "+dissURL); } // Dispatch backend service URL request authenticating as necessary based on beSecurity configuration dissemination = getDisseminationContent(dissURL, context, beServiceCallUsername, beServiceCallPassword); } } else if (protocolType.equalsIgnoreCase("soap")) { // FIXME!! future handling of soap bindings. String message = "[DisseminationService] Protocol type: " + protocolType + "NOT yet implemented"; LOG.error(message); throw new DisseminationException(message); } else { String message = "[DisseminationService] Protocol type: " + protocolType + "NOT supported."; LOG.error(message); throw new DisseminationException(message); } } else { // DisseminationBindingInfo was empty so there was no information // provided to construct a dissemination. String message = "[DisseminationService] Dissemination Binding "+ "Info contained no data"; LOG.error(message); throw new DisseminationBindingInfoNotFoundException(message); } return dissemination; } /** *

Datastream locations are considered privileged information by the * Fedora repository. To prevent disclosing physical datastream locations * to external mechanism services, a proxy is used to disguise the datastream * locations. This method generates a temporary ID that maps to the * physical datastream location and registers this information in a * memory resident hashtable for subsequent resolution of the physical * datastream location. The servlet DatastreamResolverServlet * provides the proxy resolution service for datastreams.

*

*

The format of the tempID is derived from java.sql.Timestamp * with an arbitrary counter appended to the end to insure uniqueness. The * syntax is of the form: *

    *

    YYYY-MM-DD HH:mm:ss.mmm:dddddd where

    *
      *
    • YYYY - year (1900-8099)
    • *
    • MM - month (01-12)
    • *
    • DD - day (01-31)
    • *
    • hh - hours (0-23)
    • *
    • mm - minutes (0-59)
    • *
    • ss - seconds (0-59)
    • *
    • mmm - milliseconds (0-999)
    • *
    • dddddd - incremental counter (0-999999)
    • *
    *
* * @param dsLocation The physical location of the datastream. * @param dsControlGroupType The type of the datastream. * @return A temporary ID used to reference the physical location of the * specified datastream * @throws ServerException If an error occurs in registering a datastream * location. */ public String registerDatastreamLocation(String dsLocation, String dsControlGroupType, String beServiceCallbackRole, String methodName) throws ServerException { String tempID = null; Timestamp timeStamp = null; if (counter > 999999) counter = 0; long currentTime = new Timestamp(new Date().getTime()).getTime(); long expireLimit = currentTime - (long)datastreamExpirationLimit*1000; String dsMediatedServletPath = null; String dsMediatedCallbackHost = null; try { // Remove any datastream registrations that have expired. // The expiration limit can be adjusted using the Fedora config parameter // named "datastreamExpirationLimit" which is in seconds. for ( Enumeration e = dsRegistry.keys(); e.hasMoreElements(); ) { String key = (String)e.nextElement(); timeStamp = Timestamp.valueOf(extractTimestamp(key)); if (expireLimit > timeStamp.getTime()) { dsRegistry.remove(key); LOG.debug("DatastreamMediationKey removed from Hash: " + key); } } // Register datastream. if (tempID == null) { timeStamp = new Timestamp(new Date().getTime()); tempID = timeStamp.toString()+":"+counter++; DatastreamMediation dm = new DatastreamMediation(); dm.mediatedDatastreamID = tempID; dm.dsLocation = dsLocation; dm.dsControlGroupType = dsControlGroupType; dm.methodName = methodName; // See if datastream reference is to fedora server itself or an external location. // M and X type datastreams always reference fedora server. With E type datastreams // we must examine URL to see if this is referencing a remote datastream or is // simply a callback to the fedora server. If the reference is remote, then use // the role of the backend service that will make a callback for this datastream. // If the referenc s to the fedora server, use the special role of "fedoraInternalCall-1" to // denote that the callback will come from the fedora server itself. String beServiceRole = null; if ( ServerUtility.isURLFedoraServer(dsLocation) || dsControlGroupType.equals("M") || dsControlGroupType.equals("X") ) { beServiceRole = BackendPolicies.FEDORA_INTERNAL_CALL; } else { beServiceRole = beServiceCallbackRole; } // Store beSecurity info in hash Hashtable beHash = m_beSS.getSecuritySpec(beServiceRole, methodName); boolean beServiceCallbackBasicAuth = new Boolean((String) beHash.get("callbackBasicAuth")).booleanValue(); boolean beServiceCallBasicAuth = new Boolean((String) beHash.get("callBasicAuth")).booleanValue(); boolean beServiceCallbackSSL = new Boolean((String) beHash.get("callbackSSL")).booleanValue(); boolean beServiceCallSSL = new Boolean((String) beHash.get("callSSL")).booleanValue(); String beServiceCallUsername = (String) beHash.get("callUsername"); String beServiceCallPassword = (String) beHash.get("callPassword"); if (LOG.isDebugEnabled()) { LOG.debug("******************Registering datastream dsLocation: "+dsLocation); LOG.debug("******************Registering datastream dsControlGroupType: "+dsControlGroupType); LOG.debug("******************Registering datastream beServiceRole: "+beServiceRole); LOG.debug("******************Registering datastream beServiceCallbackBasicAuth: "+beServiceCallbackBasicAuth); LOG.debug("******************Registering datastream beServiceCallBasicAuth: "+beServiceCallBasicAuth); LOG.debug("******************Registering datastream beServiceCallbackSSL: "+beServiceCallbackSSL); LOG.debug("******************Registering datastream beServiceCallSSL: "+beServiceCallSSL); LOG.debug("******************Registering datastream beServiceCallUsername: "+beServiceCallUsername); LOG.debug("******************Registering datastream beServiceCallPassword: "+beServiceCallPassword); } dm.callbackRole = beServiceRole; dm.callUsername = beServiceCallUsername; dm.callPassword = beServiceCallPassword; dm.callbackBasicAuth = beServiceCallbackBasicAuth; dm.callBasicAuth = beServiceCallBasicAuth; dm.callbackSSL = beServiceCallbackSSL; dm.callSSL = beServiceCallSSL; dsRegistry.put(tempID, dm); LOG.debug("DatastreammediationKey added to Hash: " + tempID); } } catch(Throwable th) { throw new DisseminationException("[DisseminationService] register" + "DatastreamLocation: " + "returned an error. The underlying error was a " + th.getClass().getName() + " The message " + "was \"" + th.getMessage() + "\" ."); } // Replace the blank between date and time with the character "T". return tempID.replaceAll(" ","T"); } /** *

The tempID that is used for datastream mediation consists of a * Timestamp plus a counter appended to the end to insure uniqueness. * This method is a utility method used to extract the Timestamp portion * from the tempID by stripping off the arbitrary counter at the end of * the string.

* * @param tempID The tempID to be extracted. * @return The extracted Timestamp value as a string. */ public String extractTimestamp(String tempID) { StringBuffer sb = new StringBuffer(); sb.append(tempID); sb.replace(tempID.lastIndexOf(":"),tempID.length(),""); return sb.toString(); } /** *

Performs simple string replacement using regular expressions. * All matching occurrences of the pattern string will be replaced in the * input string by the replacement string. * * @param inputString The source string. * @param patternString The regular expression pattern. * @param replaceString The replacement string. * @return The source string with substitutions. */ private String substituteString(String inputString, String patternString, String replaceString) { Pattern pattern = Pattern.compile(patternString); Matcher m = pattern.matcher(inputString); return m.replaceAll(replaceString); } /** *

Removes any optional userInputParms which remain in the dissemination * URL. This occurs when a method has optional parameters and the user does * not supply a value for one or more of the optional parameters. The result * is a syntax similar to "parm=(PARM_BIND_KEY)". This method removes these * non-supplied optional parameters from the string.

* * @param dissURL String to be processed. * @return An edited string with parameters removed where no value was * specified for any optional parameters. */ private String stripParms(String dissURL) { String requestURI = dissURL.substring(0,dissURL.indexOf("?")+1); String parmString = dissURL.substring(dissURL.indexOf("?")+1,dissURL.length()); String[] parms = parmString.split("&"); StringBuffer sb = new StringBuffer(); for (int i=0; iConverts the internal dsLocation used by managed and XML type datastreams * to the corresponding Default Dissemination request that will return the * datastream contents.

* * @param internalDSLocation - dsLocation of the Managed or XML type datastream. * @param PID - the persistent identifier of the digital object. * @return - A URL corresponding to the Default Dissemination request for the * specified datastream. * @throws ServerException - If anything goes wrong during the conversion attempt. */ private String resolveInternalDSLocation(Context context, String internalDSLocation, String PID, String callbackHost) throws ServerException { if (callbackHost == null || callbackHost.equals("")) { throw new DisseminationException("[DisseminationService] was unable to " + "resolve the base URL of the Fedora Server. The URL specified was: \"" + callbackHost + "\". This information is required by the Dissemination Service."); } String[] s = internalDSLocation.split("\\+"); String dsLocation = null; if (s.length == 3) { DOReader doReader = m_manager.getReader(Server.GLOBAL_CHOICE, context, PID); Datastream d = (Datastream) doReader.getDatastream(s[1], s[2]); dsLocation = callbackHost +"/fedora/get/"+s[0]+"/"+s[1]+"/" +DateUtility.convertDateToString(d.DSCreateDT); } else { String message = "[DisseminationService] An error has occurred. " + "The internal dsLocation: \"" + internalDSLocation + "\" is " + "not in the required format of: " + "\"doPID+DSID+DSVERSIONID\" ."; LOG.error(message); throw new GeneralException(message); } LOG.debug("********** Resolving Internal Datastream dsLocation: "+dsLocation); return dsLocation; } public static void printBindingInfo(DisseminationBindingInfo[] info) { for (int i = 0; i < info.length; i++) { LOG.debug("DisseminationBindingInfo[" + i + "]:"); LOG.debug(" DSBindKey : " + info[i].DSBindKey); LOG.debug(" dsLocation : " + info[i].dsLocation); LOG.debug(" dsControlGroupType : " + info[i].dsControlGroupType); LOG.debug(" dsID : " + info[i].dsID); LOG.debug(" dsVersionID : " + info[i].dsVersionID); LOG.debug(" AddressLocation : " + info[i].AddressLocation); LOG.debug(" OperationLocation : " + info[i].OperationLocation); LOG.debug(" ProtocolType : " + info[i].ProtocolType); LOG.debug(" dsState : " + info[i].dsState); for (int j = 0; j < info[i].methodParms.length; j++) { MethodParmDef def = info[i].methodParms[j]; LOG.debug(" MethodParamDef[" + j + "]:"); LOG.debug(" parmName : " + def.parmName); LOG.debug(" parmDefaultValue : " + def.parmDefaultValue); LOG.debug(" parmRequired : " + def.parmRequired); LOG.debug(" parmLabel : " + def.parmLabel); LOG.debug(" parmPassBy : " + def.parmPassBy); for (int k = 0; k < def.parmDomainValues.length; k++) { LOG.debug(" parmDomainValue : " + def.parmDomainValues[k]); } } } } /** * Get a MIMETypedStream for the given URL. * * If user or password are null, basic authentication will * not be attempted. */ private static MIMETypedStream get(String url, String user, String pass) throws GeneralException { LOG.debug("DisseminationService.get(" + url + ")"); try { HttpInputStream response = s_http.get(url, true, user, pass); String mimeType = response.getResponseHeaderValue("Content-Type", "text/plain"); Property[] headerArray = toPropertyArray( response.getResponseHeaders()); return new MIMETypedStream(mimeType, response, headerArray); } catch (Exception e) { throw new GeneralException("Error getting " + url, e); } } /** * Convert the given HTTP Headers to an array of * Property objects. */ private static Property[] toPropertyArray(Header[] headers) { Property[] props = new Property[headers.length]; for (int i = 0; i < headers.length; i++) { props[i] = new Property(); props[i].name = headers[i].getName(); props[i].value = headers[i].getValue(); } return props; } /** * A method that reads the contents of the specified URL and returns the * result as a MIMETypedStream * * @param url The URL of the external content. * @return A MIME-typed stream. * @throws HttpServiceNotFoundException If the URL connection could not * be established. */ public MIMETypedStream getDisseminationContent(String url, Context context, String user, String pass) throws GeneralException, HttpServiceNotFoundException { return get(url, user, pass); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy