Ejemplo n.º 1
0
  /**
   * Return the locked document lock. Must be called before beforeUpdate().
   *
   * @param parameters incoming Ajax request
   * @return the document lock, already locked
   */
  public Lock acquireDocumentLock(RequestParameters parameters) {
    assert parameters.getUUID() != null;

    // Check that the session is associated with the requested UUID. This enforces the rule that an
    // incoming request
    // for a given UUID must belong to the same session that created the document. If the session
    // expires, the
    // key goes away as well, and the key won't be present. If we don't do this check, the XForms
    // server might
    // handle requests for a given UUID within a separate session, therefore providing access to
    // other sessions,
    // which is not desirable. Further, we now have a lock stored in the session.
    final Lock lock = getDocumentLock(parameters.getUUID());
    if (lock == null)
      throw new OXFException("Document session has expired. Unable to process incoming request.");

    // Lock document for at most the max retry delay plus an increment
    try {
      final boolean acquired =
          lock.tryLock(
              XFormsProperties.getAjaxTimeout() + XFormsProperties.getRetryDelayIncrement(),
              TimeUnit.MILLISECONDS);
      if (acquired) return lock;
      else return null;
    } catch (InterruptedException e) {
      throw new OXFException(e);
    }
  }
Ejemplo n.º 2
0
  /**
   * Find or restore a document based on an incoming request.
   *
   * <p>Implementation: try cache first, then restore from store if not found.
   *
   * <p>If found in cache, document is removed from cache.
   *
   * @param parameters update parameters
   * @param isInitialState whether to return the initial state, otherwise return the current state
   * @param disableUpdates whether to disable updates (for recreating initial document upon browser
   *     back)
   * @return document, either from cache or from state information
   */
  public XFormsContainingDocument findOrRestoreDocument(
      RequestParameters parameters, boolean isInitialState, boolean disableUpdates) {

    // Try cache first unless the initial state is requested
    if (!isInitialState) {
      if (XFormsProperties.isCacheDocument()) {
        // Try to find the document in cache using the UUID
        // NOTE: If the document has cache.document="false", then it simply won't be found in the
        // cache, but
        // we can't know that the property is set to false before trying.
        final XFormsContainingDocument cachedDocument =
            XFormsDocumentCache.instance().takeDocument(parameters.getUUID());
        if (cachedDocument != null) {
          // Found in cache
          indentedLogger.logDebug(
              LOG_TYPE, "Document cache enabled. Returning document from cache.");
          return cachedDocument;
        }
        indentedLogger.logDebug(
            LOG_TYPE,
            "Document cache enabled. Document not found in cache. Retrieving state from store.");
      } else {
        indentedLogger.logDebug(LOG_TYPE, "Document cache disabled. Retrieving state from store.");
      }
    } else {
      indentedLogger.logDebug(
          LOG_TYPE, "Initial document state requested. Retrieving state from store.");
    }

    // Must recreate from store
    return createDocumentFromStore(parameters, isInitialState, disableUpdates);
  }
Ejemplo n.º 3
0
 /**
  * Return the dynamic state string to send to the client in the HTML page.
  *
  * @param containingDocument containing document
  * @return encoded state
  */
 public String getClientEncodedDynamicState(XFormsContainingDocument containingDocument) {
   final String dynamicStateString;
   {
     if (containingDocument.getStaticState().isServerStateHandling()) {
       // Return UUID
       dynamicStateString = null;
     } else {
       // Return full encoded state
       dynamicStateString =
           DynamicState.encodeDocumentToString(
               containingDocument, XFormsProperties.isGZIPState(), true);
     }
   }
   return dynamicStateString;
 }
Ejemplo n.º 4
0
  public void replace(
      PropertyContext propertyContext,
      ConnectionResult connectionResult,
      XFormsModelSubmission.SubmissionParameters p,
      XFormsModelSubmission.SecondPassParameters p2) {

    // Set new instance document to replace the one submitted

    final XFormsInstance replaceInstanceNoTargetref =
        submission.findReplaceInstanceNoTargetref(p.refInstance);
    if (replaceInstanceNoTargetref == null) {

      // Replacement instance or node was specified but not found
      //
      // Not sure what's the right thing to do with 1.1, but this could be done
      // as part of the model's static analysis if the instance value is not
      // obtained through AVT, and dynamically otherwise. However, in the dynamic
      // case, I think that this should be a (currently non-specified by XForms)
      // xforms-binding-error.

      submission
          .getXBLContainer(containingDocument)
          .dispatchEvent(
              propertyContext, new XFormsBindingExceptionEvent(containingDocument, submission));
    } else {

      final NodeInfo destinationNodeInfo =
          submission.evaluateTargetRef(
              propertyContext,
              p.xpathContext,
              replaceInstanceNoTargetref,
              p.submissionElementContextItem);

      if (destinationNodeInfo == null) {
        // Throw target-error

        // XForms 1.1: "If the processing of the targetref attribute fails,
        // then submission processing ends after dispatching the event
        // xforms-submit-error with an error-type of target-error."

        throw new XFormsSubmissionException(
            submission,
            "targetref attribute doesn't point to an element for replace=\"instance\".",
            "processing targetref attribute",
            new XFormsSubmitErrorEvent(
                containingDocument,
                propertyContext,
                submission,
                XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR,
                connectionResult));
      }

      // This is the instance which is effectively going to be updated
      final XFormsInstance updatedInstance =
          containingDocument.getInstanceForNode(destinationNodeInfo);
      if (updatedInstance == null) {
        throw new XFormsSubmissionException(
            submission,
            "targetref attribute doesn't point to an element in an existing instance for replace=\"instance\".",
            "processing targetref attribute",
            new XFormsSubmitErrorEvent(
                containingDocument,
                propertyContext,
                submission,
                XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR,
                connectionResult));
      }

      // Whether the destination node is the root element of an instance
      final boolean isDestinationRootElement =
          updatedInstance.getInstanceRootElementInfo().isSameNodeInfo(destinationNodeInfo);
      if (p2.isReadonly && !isDestinationRootElement) {
        // Only support replacing the root element of an instance when using a shared instance
        throw new XFormsSubmissionException(
            submission,
            "targetref attribute must point to instance root element when using read-only instance replacement.",
            "processing targetref attribute",
            new XFormsSubmitErrorEvent(
                containingDocument,
                propertyContext,
                submission,
                XFormsSubmitErrorEvent.ErrorType.TARGET_ERROR,
                connectionResult));
      }

      final IndentedLogger detailsLogger = getDetailsLogger(p, p2);

      // Obtain root element to insert
      final NodeInfo newDocumentRootElement;
      final XFormsInstance newInstance;
      {
        // Create resulting instance whether entire instance is replaced or not, because this:
        // 1. Wraps a Document within a DocumentInfo if needed
        // 2. Performs text nodes adjustments if needed
        if (!p2.isReadonly) {
          // Resulting instance must not be read-only

          if (detailsLogger.isDebugEnabled())
            detailsLogger.logDebug(
                "",
                "replacing instance with mutable instance",
                "instance",
                updatedInstance.getEffectiveId());

          newInstance =
              new XFormsInstance(
                  updatedInstance.getEffectiveModelId(),
                  updatedInstance.getId(),
                  (Document) resultingDocument,
                  connectionResult.resourceURI,
                  resultingRequestBodyHash,
                  p2.username,
                  p2.password,
                  p2.isCache,
                  p2.timeToLive,
                  updatedInstance.getValidation(),
                  p2.isHandleXInclude,
                  XFormsProperties.isExposeXPathTypes(containingDocument));
        } else {
          // Resulting instance must be read-only

          if (detailsLogger.isDebugEnabled())
            detailsLogger.logDebug(
                "",
                "replacing instance with read-only instance",
                "instance",
                updatedInstance.getEffectiveId());

          newInstance =
              new ReadonlyXFormsInstance(
                  updatedInstance.getEffectiveModelId(),
                  updatedInstance.getId(),
                  (DocumentInfo) resultingDocument,
                  connectionResult.resourceURI,
                  resultingRequestBodyHash,
                  p2.username,
                  p2.password,
                  p2.isCache,
                  p2.timeToLive,
                  updatedInstance.getValidation(),
                  p2.isHandleXInclude,
                  XFormsProperties.isExposeXPathTypes(containingDocument));
        }
        newDocumentRootElement = newInstance.getInstanceRootElementInfo();
      }

      // Perform insert/delete. This will dispatch xforms-insert/xforms-delete events.
      // "the replacement is performed by an XForms action that performs some
      // combination of node insertion and deletion operations that are
      // performed by the insert action (10.3 The insert Element) and the
      // delete action"

      if (isDestinationRootElement) {
        // Optimized insertion for instance root element replacement

        // Handle new instance and associated event markings
        final XFormsModel replaceModel = newInstance.getModel(containingDocument);
        replaceModel.handleUpdatedInstance(propertyContext, newInstance, newDocumentRootElement);

        // Dispatch xforms-delete event
        // NOTE: Do NOT dispatch so we are compatible with the regular root element replacement
        // (see below). In the future, we might want to dispatch this, especially if
        // XFormsInsertAction dispatches xforms-delete when removing the root element
        // updatedInstance.getXBLContainer(containingDocument).dispatchEvent(pipelineContext, new
        // XFormsDeleteEvent(updatedInstance, Collections.singletonList(destinationNodeInfo), 1));

        // Dispatch xforms-insert event
        // NOTE: use the root node as insert location as it seems to make more sense than pointing
        // to the earlier root element
        newInstance
            .getXBLContainer(containingDocument)
            .dispatchEvent(
                propertyContext,
                new XFormsInsertEvent(
                    containingDocument,
                    newInstance,
                    Collections.singletonList((Item) newDocumentRootElement),
                    null,
                    newDocumentRootElement.getDocumentRoot(),
                    "after",
                    null,
                    null,
                    true));

      } else {
        // Generic insertion

        final List<NodeInfo> destinationCollection = Collections.singletonList(destinationNodeInfo);

        // Perform the insertion

        // Insert before the target node, so that the position of the inserted node
        // wrt its parent does not change after the target node is removed
        final List insertedNode =
            XFormsInsertAction.doInsert(
                propertyContext,
                containingDocument,
                detailsLogger,
                "before",
                destinationCollection,
                destinationNodeInfo.getParent(),
                Collections.singletonList(newDocumentRootElement),
                1,
                false,
                true);

        if (!destinationNodeInfo
            .getParent()
            .isSameNodeInfo(destinationNodeInfo.getDocumentRoot())) {
          // The node to replace is NOT a root element

          // Perform the deletion of the selected node
          XFormsDeleteAction.doDelete(
              propertyContext, containingDocument, detailsLogger, destinationCollection, 1, true);
        }

        // Perform model instance update
        // Handle new instance and associated event markings
        // NOTE: The inserted node NodeWrapper.index might be out of date at this point because:
        // * doInsert() dispatches an event which might itself change the instance
        // * doDelete() does as well
        // Does this mean that we should check that the node is still where it should be?
        final XFormsModel updatedModel = updatedInstance.getModel(containingDocument);
        updatedModel.handleUpdatedInstance(
            propertyContext, updatedInstance, (NodeInfo) insertedNode.get(0));
      }

      // Dispatch xforms-submit-done
      dispatchSubmitDone(propertyContext, connectionResult);
    }
  }
  public void startElement(String uri, String localname, String qName, Attributes attributes)
      throws SAXException {

    namespaceSupport.startElement();

    // Handle location data
    if (locationData == null && locator != null && mustOutputFirstElement) {
      final String systemId = locator.getSystemId();
      if (systemId != null) {
        locationData =
            new LocationData(systemId, locator.getLineNumber(), locator.getColumnNumber());
      }
    }

    // Check for XForms or extension namespaces
    final boolean isXForms = XFormsConstants.XFORMS_NAMESPACE_URI.equals(uri);
    final boolean isXXForms = XFormsConstants.XXFORMS_NAMESPACE_URI.equals(uri);
    final boolean isEXForms = XFormsConstants.EXFORMS_NAMESPACE_URI.equals(uri);
    final boolean isXBL = XFormsConstants.XBL_NAMESPACE_URI.equals(uri);
    final boolean isXHTML = XMLConstants.XHTML_NAMESPACE_URI.equals(uri);

    // TODO: how else can we handle components?
    // NOTE: Here we have an issue identifying which elements must have content preserved. For
    // example, an element
    // to which an XBL binding is applied should be preserved, because XBL template processing take
    // place during
    // static state analysis. In XFormsDocumentAnnotatorContentHandler, we detect XBL bindings.
    // Should we do the
    // same here again? It is wasteful to do it twice. Possibly, XFDACH could pass this information
    // here since
    // it already does all the work to detect content preservation. E.g. custom attribute.

    //        final boolean isXFormsOrExtension = isXForms || isXXForms || isEXForms || isXBL;
    final boolean isXFormsOrExtension = !isXHTML && !"".equals(uri); // see NOTE above
    final boolean isExtension =
        isXFormsOrExtension && !isXForms && !isXXForms && !isEXForms && !isXBL; // see NOTE above

    // Handle xml:base
    if (!inXFormsOrExtension) {
      final String xmlBaseAttribute = attributes.getValue(XMLConstants.XML_URI, "base");
      if (xmlBaseAttribute == null) {
        xmlBaseStack.push(xmlBaseStack.peek());
      } else {
        try {
          final URI currentXMLBaseURI = xmlBaseStack.peek();
          xmlBaseStack.push(
              currentXMLBaseURI
                  .resolve(new URI(xmlBaseAttribute))
                  .normalize()); // normalize to remove "..", etc.
        } catch (URISyntaxException e) {
          throw new ValidationException(
              "Error creating URI from: '"
                  + xmlBaseStack.peek()
                  + "' and '"
                  + xmlBaseAttribute
                  + "'.",
              e,
              new LocationData(locator));
        }
      }
    }

    // Handle properties of the form @xxforms:* when outside of models or controls
    if (!inXFormsOrExtension && !isXFormsOrExtension) {
      final int attributesCount = attributes.getLength();
      for (int i = 0; i < attributesCount; i++) {
        final String attributeURI = attributes.getURI(i);
        if (XFormsConstants.XXFORMS_NAMESPACE_URI.equals(attributeURI)) {
          // Found xxforms:* attribute
          final String attributeLocalName = attributes.getLocalName(i);
          // Only take the first occurrence into account, and make sure the property is supported
          if (properties.get(attributeLocalName) == null
              && XFormsProperties.getPropertyDefinition(attributeLocalName) != null) {
            properties.put(attributeLocalName, attributes.getValue(i));
          }
        }
      }
    }

    if (level > 0 || !ignoreRootElement) {

      // Start extracting model or controls
      if (!inXFormsOrExtension && isXFormsOrExtension) {

        inXFormsOrExtension = true;
        xformsLevel = level;

        outputFirstElementIfNeeded();

        // Add xml:base on element
        attributes =
            XMLUtils.addOrReplaceAttribute(
                attributes, XMLConstants.XML_URI, "xml", "base", getCurrentBaseURI());

        sendStartPrefixMappings();
      }

      // Check for preserved content
      if (inXFormsOrExtension && !inPreserve) {
        // TODO: Just warn?
        if (isXXForms) {
          // Check that we are getting a valid xxforms:* element
          if (XFormsConstants.ALLOWED_XXFORMS_ELEMENTS.get(localname) == null
              && !XFormsActions.isActionName(XFormsConstants.XXFORMS_NAMESPACE_URI, localname))
            throw new ValidationException(
                "Invalid extension element in XForms document: " + qName,
                new LocationData(locator));
        } else if (isEXForms) {
          // Check that we are getting a valid exforms:* element
          if (XFormsConstants.ALLOWED_EXFORMS_ELEMENTS.get(localname) == null)
            throw new ValidationException(
                "Invalid eXForms element in XForms document: " + qName, new LocationData(locator));
        } else if (isXBL) {
          // Check that we are getting a valid xbl:* element
          if (XFormsConstants.ALLOWED_XBL_ELEMENTS.get(localname) == null)
            throw new ValidationException(
                "Invalid XBL element in XForms document: " + qName, new LocationData(locator));
        }

        // Preserve as is the content of labels, etc., instances, and schemas
        if ((XFormsConstants.LABEL_HINT_HELP_ALERT_ELEMENT.get(localname)
                        != null // labels, etc. may contain XHTML
                    || "instance".equals(localname))
                && isXForms // XForms instances
            || "schema".equals(localname) && XMLConstants.XSD_URI.equals(uri) // XML schemas
            || "xbl".equals(localname)
                && isXBL // preserve everything under xbl:xbl so that templates may be processed by
                         // static state
            || isExtension) {
          inPreserve = true;
          preserveLevel = level;
        }
      }

      // We are within preserved content or we output regular XForms content
      if (inXFormsOrExtension && (inPreserve || isXFormsOrExtension)) {
        super.startElement(uri, localname, qName, attributes);
      }
    } else {
      // Just open the root element
      outputFirstElementIfNeeded();
      sendStartPrefixMappings();
      super.startElement(uri, localname, qName, attributes);
    }

    level++;
  }
Ejemplo n.º 6
0
 // NOTE: Duplicated in XXFormsDialogControl
 public boolean isXForms11Switch() {
   final String localXForms11Switch =
       element().attributeValue(XFormsConstants.XXFORMS_XFORMS11_SWITCH_QNAME);
   if (localXForms11Switch != null) return Boolean.parseBoolean(localXForms11Switch);
   else return XFormsProperties.isXForms11Switch(containingDocument());
 }
  public SubmissionResult connect(
      final PropertyContext propertyContext,
      final XFormsModelSubmission.SubmissionParameters p,
      final XFormsModelSubmission.SecondPassParameters p2,
      final XFormsModelSubmission.SerializationParameters sp)
      throws Exception {
    // Get the instance from shared instance cache
    // This can only happen is method="get" and replace="instance" and xxforms:cache="true" or
    // xxforms:shared="application"

    // Convert URL to string
    final String absoluteResolvedURLString;
    {
      final ExternalContext externalContext = getExternalContext(propertyContext);
      final URL absoluteResolvedURL =
          getResolvedSubmissionURL(
              propertyContext, externalContext, p2.actionOrResource, sp.queryString);
      absoluteResolvedURLString = absoluteResolvedURL.toExternalForm();
    }

    // Compute a hash of the body if needed
    final String requestBodyHash;
    if (sp.messageBody != null) {
      requestBodyHash = SecureUtils.digestBytes(sp.messageBody, "MD5", "hex");
    } else {
      requestBodyHash = null;
    }

    // Parameters to callable
    final String submissionEffectiveId = submission.getEffectiveId();
    final String instanceStaticId;
    final String modelEffectiveId;
    final String validation;
    {
      // Find and check replacement location
      final XFormsInstance updatedInstance = checkInstanceToUpdate(propertyContext, p);

      instanceStaticId = updatedInstance.getId();
      modelEffectiveId = updatedInstance.getEffectiveModelId();
      validation = updatedInstance.getValidation();
    }
    final boolean isReadonly = p2.isReadonly;
    final boolean handleXInclude = p2.isHandleXInclude;
    final long timeToLive = p2.timeToLive;

    // Create new logger as the submission might be asynchronous
    final IndentedLogger submissionLogger =
        new IndentedLogger(containingDocument.getIndentedLogger());

    // Obtain replacer
    // Pass a pseudo connection result which contains information used by getReplacer()
    // We know that we will get an InstanceReplacer
    final ConnectionResult connectionResult =
        createPseudoConnectionResult(absoluteResolvedURLString);
    final InstanceReplacer replacer =
        (InstanceReplacer) submission.getReplacer(propertyContext, connectionResult, p);

    // Try from cache first
    final XFormsInstance cacheResult =
        XFormsServerSharedInstancesCache.instance()
            .findConvertNoLoad(
                propertyContext,
                submissionLogger,
                instanceStaticId,
                modelEffectiveId,
                absoluteResolvedURLString,
                requestBodyHash,
                isReadonly,
                handleXInclude,
                XFormsProperties.isExposeXPathTypes(containingDocument));

    if (cacheResult != null) {
      // Result was immediately available, so return it right away
      // The purpose of this is to avoid starting a new thread in asynchronous mode if the instance
      // is already in cache

      // Here we cheat a bit: instead of calling generically deserialize(), we directly set the
      // instance document
      replacer.setInstance(cacheResult);

      // Return result
      return new SubmissionResult(submissionEffectiveId, replacer, connectionResult);
    } else {
      // Create callable for synchronous or asynchronous loading
      final Callable<SubmissionResult> callable =
          new Callable<SubmissionResult>() {
            public SubmissionResult call() {
              try {
                final XFormsInstance newInstance =
                    XFormsServerSharedInstancesCache.instance()
                        .findConvert(
                            propertyContext,
                            submissionLogger,
                            instanceStaticId,
                            modelEffectiveId,
                            absoluteResolvedURLString,
                            requestBodyHash,
                            isReadonly,
                            handleXInclude,
                            XFormsProperties.isExposeXPathTypes(containingDocument),
                            timeToLive,
                            validation,
                            new XFormsServerSharedInstancesCache.Loader() {
                              public ReadonlyXFormsInstance load(
                                  PropertyContext propertyContext,
                                  String instanceStaticId,
                                  String modelEffectiveId,
                                  String instanceSourceURI,
                                  boolean handleXInclude,
                                  long timeToLive,
                                  String validation) {

                                // Call regular submission
                                SubmissionResult submissionResult = null;
                                try {
                                  // Run regular submission but force synchronous execution and
                                  // readonly result
                                  final XFormsModelSubmission.SecondPassParameters updatedP2 =
                                      p2.amend(false, true);
                                  submissionResult =
                                      new RegularSubmission(submission)
                                          .connect(propertyContext, p, updatedP2, sp);

                                  // Check if the connection returned a throwable
                                  final Throwable throwable = submissionResult.getThrowable();
                                  if (throwable != null) {
                                    // Propagate
                                    throw new ThrowableWrapper(
                                        throwable, submissionResult.getConnectionResult());
                                  } else {
                                    // There was no throwable
                                    // We know that RegularSubmission returns a Replacer with an
                                    // instance document
                                    final DocumentInfo documentInfo =
                                        (DocumentInfo)
                                            ((InstanceReplacer) submissionResult.getReplacer())
                                                .getResultingDocument();

                                    // Create new shared instance
                                    return new ReadonlyXFormsInstance(
                                        modelEffectiveId,
                                        instanceStaticId,
                                        documentInfo,
                                        instanceSourceURI,
                                        updatedP2.username,
                                        updatedP2.password,
                                        true,
                                        timeToLive,
                                        validation,
                                        handleXInclude,
                                        XFormsProperties.isExposeXPathTypes(containingDocument));
                                  }
                                } catch (ThrowableWrapper throwableWrapper) {
                                  // In case we just threw it above, just propagate
                                  throw throwableWrapper;
                                } catch (Throwable throwable) {
                                  // Exceptions are handled further down
                                  throw new ThrowableWrapper(
                                      throwable,
                                      (submissionResult != null)
                                          ? submissionResult.getConnectionResult()
                                          : null);
                                }
                              }
                            });

                // Here we cheat a bit: instead of calling generically deserialize(), we directly
                // set the DocumentInfo
                replacer.setInstance(newInstance);

                // Return result
                return new SubmissionResult(submissionEffectiveId, replacer, connectionResult);
              } catch (ThrowableWrapper throwableWrapper) {
                // The ThrowableWrapper was thrown within the inner load() method above
                return new SubmissionResult(
                    submissionEffectiveId,
                    throwableWrapper.getThrowable(),
                    throwableWrapper.getConnectionResult());
              } catch (Throwable throwable) {
                // Any other throwable
                return new SubmissionResult(submissionEffectiveId, throwable, null);
              }
            }
          };

      // Submit the callable
      // This returns null if the execution is asynchronous
      return submitCallable(p, p2, callable);
    }
  }
  public static void outputResponseDocument(
      final PipelineContext pipelineContext,
      final ExternalContext externalContext,
      final IndentedLogger indentedLogger,
      final SAXStore annotatedDocument,
      final XFormsContainingDocument containingDocument,
      final XMLReceiver xmlReceiver)
      throws SAXException, IOException {

    final List<XFormsContainingDocument.Load> loads = containingDocument.getLoadsToRun();
    if (containingDocument.isGotSubmissionReplaceAll()) {
      // 1. Got a submission with replace="all"

      // NOP: Response already sent out by a submission
      // TODO: modify XFormsModelSubmission accordingly
      indentedLogger.logDebug("", "handling response for submission with replace=\"all\"");
    } else if (loads != null && loads.size() > 0) {
      // 2. Got at least one xforms:load

      // Send redirect out

      // Get first load only
      final XFormsContainingDocument.Load load = loads.get(0);

      // Send redirect
      final String redirectResource = load.getResource();
      indentedLogger.logDebug(
          "", "handling redirect response for xforms:load", "url", redirectResource);
      // Set isNoRewrite to true, because the resource is either a relative path or already contains
      // the servlet context
      externalContext.getResponse().sendRedirect(redirectResource, null, false, false, true);

      // Still send out a null document to signal that no further processing must take place
      XMLUtils.streamNullDocument(xmlReceiver);
    } else {
      // 3. Regular case: produce an XHTML document out

      final ElementHandlerController controller = new ElementHandlerController();

      // Register handlers on controller (the other handlers are registered by the body handler)
      {
        controller.registerHandler(
            XHTMLHeadHandler.class.getName(), XMLConstants.XHTML_NAMESPACE_URI, "head");
        controller.registerHandler(
            XHTMLBodyHandler.class.getName(), XMLConstants.XHTML_NAMESPACE_URI, "body");

        // Register a handler for AVTs on HTML elements
        final boolean hostLanguageAVTs =
            XFormsProperties
                .isHostLanguageAVTs(); // TODO: this should be obtained per document, but we only
        // know about this in the extractor
        if (hostLanguageAVTs) {
          controller.registerHandler(
              XXFormsAttributeHandler.class.getName(),
              XFormsConstants.XXFORMS_NAMESPACE_URI,
              "attribute");
          controller.registerHandler(
              XHTMLElementHandler.class.getName(), XMLConstants.XHTML_NAMESPACE_URI);
        }

        // Swallow XForms elements that are unknown
        controller.registerHandler(
            NullHandler.class.getName(), XFormsConstants.XFORMS_NAMESPACE_URI);
        controller.registerHandler(
            NullHandler.class.getName(), XFormsConstants.XXFORMS_NAMESPACE_URI);
        controller.registerHandler(NullHandler.class.getName(), XFormsConstants.XBL_NAMESPACE_URI);
      }

      // Set final output
      controller.setOutput(new DeferredXMLReceiverImpl(xmlReceiver));
      // Set handler context
      controller.setElementHandlerContext(
          new HandlerContext(
              controller, pipelineContext, containingDocument, externalContext, null));
      // Process the entire input
      annotatedDocument.replay(
          new ExceptionWrapperXMLReceiver(controller, "converting XHTML+XForms document to XHTML"));
    }

    containingDocument.afterInitialResponse();
  }
  private void gatherInputDependencies(
      PipelineContext pipelineContext,
      XFormsContainingDocument containingDocument,
      IndentedLogger indentedLogger,
      Stage1CacheableState stage1CacheableState) {

    final String forwardSubmissionHeaders =
        XFormsProperties.getForwardSubmissionHeaders(containingDocument);

    // Add static instance source dependencies for top-level models
    // TODO: check all models/instances
    final XFormsStaticState staticState = containingDocument.getStaticState();
    for (final Model model :
        staticState.getModelsForScope(staticState.getXBLBindings().getTopLevelScope())) {
      for (final Instance instance : model.instancesMap().values()) {
        if (instance.dependencyURL() != null) {

          final String resolvedDependencyURL =
              XFormsUtils.resolveServiceURL(
                  pipelineContext,
                  containingDocument,
                  instance.element(),
                  instance.dependencyURL(),
                  ExternalContext.Response.REWRITE_MODE_ABSOLUTE);

          if (!instance.isCacheHint()) {
            stage1CacheableState.addReference(
                null,
                resolvedDependencyURL,
                instance.xxformsUsername(),
                instance.xxformsPassword(),
                instance.xxformsPassword(),
                forwardSubmissionHeaders);

            if (indentedLogger.isDebugEnabled())
              indentedLogger.logDebug(
                  "",
                  "adding document cache dependency for non-cacheable instance",
                  "instance URI",
                  resolvedDependencyURL);

          } else {
            // Don't add the dependency as we don't want the instance URI to be hit
            // For all practical purposes, globally shared instances must remain constant!
            if (indentedLogger.isDebugEnabled())
              indentedLogger.logDebug(
                  "",
                  "not adding document cache dependency for cacheable instance",
                  "instance URI",
                  resolvedDependencyURL);
          }
        }
      }
    }

    // Set caching dependencies if the input was actually read
    // TODO: check all models/instances
    // Q: should use static dependency information instead? what about schema imports and instance
    // replacements?
    for (final XFormsModel currentModel : containingDocument.getModels()) {
      // Add schema dependencies
      final String[] schemaURIs = currentModel.getSchemaURIs();
      // TODO: We should also use dependencies computed in XFormsModelSchemaValidator.SchemaInfo
      if (schemaURIs != null) {
        for (final String currentSchemaURI : schemaURIs) {
          if (indentedLogger.isDebugEnabled())
            indentedLogger.logDebug(
                "", "adding document cache dependency for schema", "schema URI", currentSchemaURI);

          stage1CacheableState.addReference(
              null,
              currentSchemaURI,
              null,
              null,
              null,
              forwardSubmissionHeaders); // TODO: support username / password on schema refs
        }
      }
    }
    // TODO: Add @src attributes from controls? Not used often.

    // Set caching dependencies for XBL inclusions
    {
      final XFormsStaticState.Metadata metadata = containingDocument.getStaticState().getMetadata();
      final Set<String> includes = metadata.getBindingsIncludes();
      if (includes != null) {
        for (final String include : includes) {
          stage1CacheableState.addReference(null, "oxf:" + include, null, null, null, null);
        }
      }
    }
  }
Ejemplo n.º 10
0
  private class StaticStateBits {

    private final boolean isLogStaticStateInput =
        XFormsProperties.getDebugLogging().contains("html-static-state");

    public final XFormsStaticState.Metadata metadata = new XFormsStaticState.Metadata();
    public final SAXStore annotatedTemplate = new SAXStore();

    public final Document staticStateDocument;
    public final String staticStateDigest;

    public StaticStateBits(
        PipelineContext pipelineContext,
        ExternalContext externalContext,
        IndentedLogger indentedLogger,
        String existingStaticStateDigest) {

      final boolean computeDigest = isLogStaticStateInput || existingStaticStateDigest == null;

      indentedLogger.startHandleOperation(
          "", "reading input", "existing digest", existingStaticStateDigest);

      final TransformerXMLReceiver documentReceiver =
          TransformerUtils.getIdentityTransformerHandler();
      final LocationDocumentResult documentResult = new LocationDocumentResult();
      documentReceiver.setResult(documentResult);

      final XMLUtils.DigestContentHandler digestReceiver =
          computeDigest ? new XMLUtils.DigestContentHandler("MD5") : null;
      final XMLReceiver extractorOutput;
      if (isLogStaticStateInput) {
        extractorOutput =
            computeDigest
                ? new TeeXMLReceiver(
                    documentReceiver, digestReceiver, getDebugReceiver(indentedLogger))
                : new TeeXMLReceiver(documentReceiver, getDebugReceiver(indentedLogger));
      } else {
        extractorOutput =
            computeDigest ? new TeeXMLReceiver(documentReceiver, digestReceiver) : documentReceiver;
      }

      // Read the input through the annotator and gather namespace mappings
      //
      // Output of annotator is:
      //
      // o annotated page template (TODO: this should not include model elements)
      // o extractor
      //
      // Output of extractor is:
      //
      // o static state document
      // o optionally: digest
      // o optionally: debug output
      //
      readInputAsSAX(
          pipelineContext,
          INPUT_ANNOTATED_DOCUMENT,
          new XFormsAnnotatorContentHandler(
              annotatedTemplate,
              new XFormsExtractorContentHandler(extractorOutput, metadata),
              metadata));

      this.staticStateDocument = documentResult.getDocument();
      this.staticStateDigest =
          computeDigest ? NumberUtils.toHexString(digestReceiver.getResult()) : null;

      assert !isLogStaticStateInput
          || existingStaticStateDigest == null
          || this.staticStateDigest.equals(existingStaticStateDigest);

      indentedLogger.endHandleOperation("computed digest", this.staticStateDigest);
    }

    private XMLReceiver getDebugReceiver(final IndentedLogger indentedLogger) {
      final TransformerXMLReceiver identity = TransformerUtils.getIdentityTransformerHandler();
      final StringBuilderWriter writer = new StringBuilderWriter();
      identity.setResult(new StreamResult(writer));

      return new ForwardingXMLReceiver(identity) {
        @Override
        public void endDocument() throws SAXException {
          super.endDocument();
          // Log out at end of document
          indentedLogger.logDebug("", "static state input", "input", writer.toString());
        }
      };
    }
  }