TNS Internal:NDR/APIRequestLifeCycle

From NSDLWiki

Jump to: navigation, search



NDR API Request Life Cycle

Definition of Struts Actions

Location: src.config.WEB-INF.struts-config.xml

Things that Run at Tomcat Startup

Struts plugins run at Tomcat startup.

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
  <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />
  • ValidatorPlugIn
    • validates that XMLInput is there and well-formed

<plug-in className="org.nsdl.repository.api.FedoraPlugIn">
  • FedoraPlugin plugin
    • creates big bang objects if not already there
    • sets up configurations and saves Configuration on the servlet context as FedoraPlugin.CONFIG
    • creates and saves NDRAccess on the servlet context as Constants.NDR_ACCESS
    • creates and saves FedoraAccess on the servlet context as FedoraPlugin.FEDORA_ACCESS

<plug-in className="org.nsdl.repository.api.auth.AuthGateway">
  <set-property property="authentication" value="on" />
  <set-property property="authorization" value="on" />
  • AuthGateway plugin
    • verifies that the NDR has been initialized
    • sets up public key management
    • sets up server path and redirect paths
    • saves itself on the servlet context as AuthGateway.GATEWAY_KEY

NOTE: AuthGateway has the method that is used to authenticate a request including validating signed headers.

Struts Definition for each API Request

Struts is used to define the API requests as Struts Actions. All actions are defined under the <action-mappings> element using an <action> element to define the API requests. A single API request may have several <action> elements defined in order to handle optional parameters to the call.

The following defines the attributes and subelements for an <action> element and how they relate to NDR API requests.

  • structure of the URL to match to the API request

  path="/add"            used for adding new objects
  path="/delete/*"       used to delete an object given the Fedora PID  (ndr:5643)
  path="/delete/*/*"     used to delete an object given the NDR handle  (2200/2010021398674T)
  • the class that processes the API request


  • where to go after processing of the API request completes

    NOTE: Each API request can have multiple forwards defined. Processing selects which forward to use by setting the forward statement in the Action class to one of the names (ex. success, error, redirect, stream, etc.)

    NOTE: The JSPs indicated by path are used to format the response that is returned to the user.

  <forward name="success"  path="/info.jsp" />       used by adds, modifies, deletes, purges, finds, some lists, translate
  <forward name="success"  path="/describe.jsp" />   used by describes
  <forward name="success"  path="/get.jsp" />        used by gets, info, some lists
  <forward name="redirect" path="/redirect.jsp" />   used by gets
  <forward name="stream"   path="/stream" />         used by gets
  <forward name="success"  path="/oai.jsp" />        used by gets for metadata

NOTE: primary content is handled outside of struts

Global Struts Definitions Affecting All API Requests

Struts is used to define the following that affects every API request...

  • additional definitions that are available for all API requests that determine where to go after processing of the API request completes
    <forward name="error" path="/error.jsp" />               globally defined for all requests
    <forward name="unavailable" path="/unavailable.html" />  globally defined for all requests

  • special processing that struts will perform in certain situations defined in the <controller> element. NDR sets two properties both of which are used for processing the incoming request.

    <set-property property="processorClass"
       value="org.nsdl.repository.api.auth.AuthRequestProcessor" />                        process non-Multipart Form requests
    <set-property property="multipartClass"
       value="org.nsdl.repository.api.request.FilePassthroughMultipartRequestHandler" />   process Multipart Form requests
    <set-property property="contentType" value="application/xml;charset=UTF-8" />

NOTE: The two request processor classes run before the Action class for the API request is called.

Question: Does AuthRequestProcessor run in all cases? For non-Multipart Form, AuthRequestProcessor runs and then the request's Action class runs. For Multipart Form, AuthRequestProcessor runs first, then FilePassthroughMultipartRequestHandler, and then the request's Action class runs. Is this correct?

Lifecycle of an API Request

!!! NOTE: NDRAccess is the key interface for accessing repository content !!!

User Action

  • User sends an API request.

Struts Processing

  • struts intercepts request

one-time setup of NDRAccess

  • struts runs init() method of controller->processorClass (org.nsdl.repository.api.auth.AuthRequestProcessor extends RequestProcessor) called I think the first time AuthRequestProcessor is used vs. each time a new request comes in
    • creates DefaultRequestProcessor
    • calls init(NDRAccess) on DefaultRequestProcessor which
      • holds NDRAccess in ndr
      • sets requestClass to XMLRequest class to serve as APIRequestProcessor for NDRAccess
      • calls initFilters()
        NOTE: considered deprecated so don't add new filters here
        • adds MetadataReferenceFilter()
        • adds URINormalizationFilter()
    • saves on servlet context as Constants.API_REQUEST_PROCESSOR;

per request setup of authentication

  • struts sends request to controller->processorClass (org.nsdl.repository.api.auth.AuthRequestProcessor extends RequestProcessor)
    • AuthRequestProcessor.processPreprocess(request,response) called automatically by struts for each new request
      • gets instance of AuthGateway from servlet context using key AuthGateway.GATEWAY_KEY (created by AuthGateway plugin initialization process)
      • calls AuthGateway.getAuthInfo(request) to validate that request is signed properly and to get AgentAuthInfo for the signing agent'
        • gets agent handle from header
        • creates an Agent NDRObject based on the handle
        • creates an instance of AgentAuthorization(NDRObject<agent>,NDRAccess,config,authorized) where agent identifies the signing agent, config is AuthGateway.m_config, authorized is only true if the signature was determined to be valid
          NOTE: m_config is created in AuthGateway.init() from a config file identified by FedoraPlugIn.CONFIG. It's not clear to me what these rules are nor where they are stored. Used later in AgentAuthorization.isAuthorizedFor(apiRequest) to check relationships
          • holds authorizedAgent = agent (passed in)
          • holds authorized = authorized (passed in)
          • holds ndr = ndr (passed in)
          • holds m_rules instance of RuleManager to hold config (passed in)
          • holds ROOT_AGENT_HANDLE (part of passed in config)
          • holds TRUSTED_AGENTS (part of passed in config)
        • returns the instance of AgentAuthorization
      • saves AgentAuthorization which implements AgentAuthInfo as request attribute with key as AuthGateway.AUTH_INFO_KEY

pre-process InputXML

  • if multi-part form, struts sends request to controller->multipartClass (org.nsdl.repository.api.request.FilePassthroughMultipartRequestHandler extends CommonsMultipartRequestHandler)
    • FilePassthroughMultipartRequestHandler.addFileParameter() called automatically by struts for each parameter in the new request that is a file
      • adds to a list of all file items
    • FilePassthroughMultipartRequestHandler.handleRequest() called automatically by struts for each new request that is a multipart form
      • adds an attribute to the request wrapper for each form item that is a file (attribute name: _file.fieldname; attribute value: ) NOTE: The file items were put on a list using addFileParameter() prior to calling handleRequest()

connect request processor to current action

  • when is setServlet() in action class called? I believe it is called just before calling execute() on the struts action
    • filter is set to DefaultRequestProcessor that extends api.request.APIRequestProcessor and that was saved on context in AuthRequestProcessor; NOTE: requestClass in DefaultRequestProcessor was set previously when that class' init() method was called above

process current action

  • struts forwards request to the class specified in the action-mappings->action that matches the URL of the API request by calling its execute() method

NDR Processing

  • run execute() method in URL's Action class which extends from GenericAction

setup for all API calls

  • calls initDataFormBean(form,request) to set requestURI and datestamp of response in the form object. Saves request URI and current timestamp (initDataFormBean method defined in GenericAction)
  • calls getNDR(request) to get the NDRAccess object saved on content in FedoraPlugin (getNDR method defined in GenericAction)
  • calls getAuth(request) for API requests that change objects in the NDR to get the AgentAuthInfo object saved on context in AuthRequestProcessor.processPreprocess() (getAuth method defined in GenericAction)

begin processing the request

  • calls getApiRequest(request) (defined in GenericAction)
    • calls filter.getRequest() inherited from DefaultRequestProcessor (filter set previously in setServlet() in GenericAction)...
      • creates an instance of the requestClass (set to XMLRequest in DefaultRequestProcessor's init() method)
        • holds static Lists of all add api requests (addRequests) and modify api requests (modifyRequests)
      • calls XMLRequest.init()
        • holds HttpServletRequest in m_request
        • holds Map of attached content in m_content
        • holds Node requestNode which is the first node in the inputXML which is expected to be the Type of the object being operated on (ex. agent, metadataProvider, aggregator, resource, metadata)
        • gets a local copy of ApiRequestPath which is an instance of ParsedRequestPath from RequestUtils.getPath(request)
          • constructor for ParsedRequestPath parses request into...
            • m_apiCall = api call (This is either in the format: /api/addAgent OR /addAgent)
            • m_handle = second parameter
            • m_path = all remaining parameters
        • holds apiCall which it gets from ApiRequestPath.getApiCall() which returns m_apiCall
        • holds NDRObjectInfo in object
          • set using new StaticObjectInfo(ApiRequestPath.getHandle()) if handle is in the URL (determined by ApiRequestPath.hasHandle()
          • OR set using new NDRObjectTemplate(Type) using local name of requestNode to determine type if handle is not available
        • holds Map params which is Map<key,value> of all parameters on the request (marked as used for debugging only)
      • calls filterRequest(XMLRequest)
        • creates processRequest which is an instance of MutableRequestWrapper(XMLRequest) which is an implements MutableRequest
          • holds request = XMLRequest.m_request
          • holds object = XMLRequest.object
          • holds requestType = Create | Update | Delete | Read based on the first word in XMLRequest.apiCall (ex. addAgent starts with add and is therefore requestType Create)
          • holds origRequest = passed in XMLRequest
          • for each command (add, delete, modify, match), gets properties, relationships, and datastreams related to that command
          • returns new instance of MutableRequestWrapper
        • calls process(processRequest) for each filter added during DefaultRequestProcessor.initFilters() (see list above one-time setup of NDRAccess
        • returns instance of MutableRequestWrapper with filters applied
      • returns results of calling filterRequest(XMLRequest) which is an instance of MutableRequest
    • returns results of calling filter.getRequest() which is an instance of MutableRequest

authenticate user for request

  • calls checkAuth(auth,apiRequest) where auth is an instance of AgentAuthorization which implements AgentAuthInfo and was returned from GenericAction.getAuth(request) and apiRequest was returned from getApiRequest(request) (checkAuth is defined in GenericAction)
    • calls AgentAuthorization.isVerified() to check if agent has been verified (value of AgentAuthInfo.authorized is set during per request setup of authentication
      • if isVerified() returns false, checkAuth() throws exception
    • calls AgentAuthorization.isAuthorizedFor(apiRequest) to check if agent is authorized for request
      • always returns true if this is root agent
      • if ! add request, calls isAuthorizedFor(NDRObjectInfo) -- gets NDRObjectInfo from apiRequest
        • return true if root agent
        • return true if NDRObjectInfo is self (ie, the signing agent's object)
        • return true if trusted agent
        • lots of authorization stuff regarding particular types of objects (see method for more info)
        • returns true if authorization passes; otherwise, false
      • if isAuthorizedFor(NDRObjectInfo) returns false, then isAuthorizedFor(apiRequest) returns false
      • checks each component of the request to see if it is a relationship and if it is, to see if the rules setup by m_config during initialization of the AuthGateway allow the relationship
      • if it makes it through all checks, returns true
      • if isAuthorizedFor() returns false, checkAuth() throws exception

request specific processing

  • highly dependent on each api request

See NDR Object Model Implementation and NDR Access and Filtering for more information on operations with objects.

tell struts processing is done

  • process errors if any
    • create message using ActionErrors errors.add
    • saveMesages(request,errors)
    • set status if needed
    • return mapping.findForward( ERROR_FORWARD ) -- defined in struts_config.xml
  • else
    • return mapping.findForward( SUCCESS_FORWARD ) -- defined in struts_config.xml

NOTE: some actions will return other forward mappings.

Personal tools