/** * 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); } }
/** * 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); }
/** * 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; }
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++; }
// 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); } } } }
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()); } }; } }