private XmlSchema GetMatchingSchema(IPipelineContext pipelineContext, Stream xmlStream) { XmlSchema schema = null; IDocumentSpec documentSpecByType = null; string docType = Utils.GetDocType(MarkableForwardOnlyEventingReadStream.EnsureMarkable(xmlStream)); try { documentSpecByType = pipelineContext.GetDocumentSpecByType(docType); } catch (Exception) { return(null); } XmlSchemaSet schemaSet = (documentSpecByType as IDocumentSpec2).GetSchemaSet(); if (schemaSet.Count == 0) { return(null); } foreach (XmlSchema schema2 in schemaSet.Schemas()) { if (docType.Contains(schema2.TargetNamespace + "#")) { schema = schema2; } } return(schema); }
/// <summary> /// Returns the <see cref="ISchemaMetadata"/> associated to the XML schema of messages of a given <see /// cref="DocumentSpec"/> type. /// </summary> /// <param name="pipelineContext"> /// The pipeline context from which the <see cref="DocumentSpec"/> can be queried. /// </param> /// <param name="docType"> /// The <see cref="DocumentSpec"/> type of the messages for which the <see cref="ISchemaMetadata"/> are to be /// returned. /// </param> /// <returns> /// The <see cref="ISchemaMetadata"/> associated to the XML Schema. /// </returns> public static ISchemaMetadata GetSchemaMetadataByType(this IPipelineContext pipelineContext, string docType) { var docSpec = pipelineContext.GetDocumentSpecByType(docType); var schemaType = Type.GetType(docSpec.DocSpecStrongName, true); return(schemaType.GetMetadata()); }
/// <summary> /// Sets the message schema strong name and message type. /// </summary> /// <param name="context">Pipeline context.</param> /// <param name="messageType">Message type.</param> /// <param name="message">Message instance.</param> /// <param name="docSpec">Document specification.</param> public static void SetDocProperties(IPipelineContext pContext, string messageType, IBaseMessage pInMsg, ref string docSpec) { try { pInMsg.Context.Promote(BtsProperties.MessageType.Name, BtsProperties.MessageType.Namespace, (object)messageType); IDocumentSpec documentSpecByType = pContext.GetDocumentSpecByType(messageType); if (documentSpecByType == null) { return; } pInMsg.Context.Write(DasmProperties.DocumentSpecName.Name, DasmProperties.DocumentSpecName.Namespace, (object)null); pInMsg.Context.Write(BtsProperties.SchemaStrongName.Name, BtsProperties.SchemaStrongName.Namespace, (object)documentSpecByType.DocSpecStrongName); docSpec = docSpec == null ? (docSpec = documentSpecByType.DocSpecName) : docSpec; } catch (DocumentSpecException ex) { // Todo log this error DECore.TraceProvider.Logger.TraceInfo(ex.ToString()); } catch (COMException ex) { // Todo log this errors DECore.TraceProvider.Logger.TraceInfo(ex.ToString()); } catch (Exception ex) { DECore.TraceProvider.Logger.TraceError(ex); throw; } }
public static bool TryGetDocumentSpecByType(this IPipelineContext pipelineContext, string docType, out IDocumentSpec documentSpec) { if (pipelineContext == null) { throw new ArgumentNullException(nameof(pipelineContext)); } try { documentSpec = pipelineContext.GetDocumentSpecByType(docType); return(true); } catch (COMException exception) { documentSpec = null; if ((uint)exception.ErrorCode == (uint)HResult.ErrorSchemaNotFound) { return(false); } if (_logger.IsWarnEnabled) { _logger.Warn($"SafeGetDocumentSpecByType({docType}) has failed.", exception); } throw; } }
public static void ProbeAndPromoteMessageType(this IBaseMessage message, IPipelineContext pipelineContext) { var messageType = message.ProbeMessageType(pipelineContext); var docSpec = pipelineContext.GetDocumentSpecByType(messageType); message.Promote(BtsProperties.MessageType, docSpec.DocType); message.Promote(BtsProperties.SchemaStrongName, docSpec.DocSpecStrongName); }
/// <summary> /// Returns the <see cref="ISchemaMetadata"/> associated to the XML schema of messages of a given <see /// cref="DocumentSpec"/> type. /// </summary> /// <param name="pipelineContext"> /// The pipeline context from which the <see cref="DocumentSpec"/> can be queried. /// </param> /// <param name="docType"> /// The <see cref="DocumentSpec"/> type of the messages for which the <see cref="ISchemaMetadata"/> are to be returned. /// </param> /// <returns> /// The <see cref="ISchemaMetadata"/> associated to the XML Schema. /// </returns> public static ISchemaMetadata GetSchemaMetadataByType(this IPipelineContext pipelineContext, string docType) { if (pipelineContext == null) { throw new ArgumentNullException(nameof(pipelineContext)); } var docSpec = pipelineContext.GetDocumentSpecByType(docType); var schemaType = Type.GetType(docSpec.DocSpecStrongName, true); return(SchemaMetadata.For(schemaType)); }
public static bool TryGetDocumentSpecByType(this IPipelineContext pipelineContext, string docType, out IDocumentSpec documentSpec) { try { documentSpec = pipelineContext.GetDocumentSpecByType(docType); return(true); } catch (COMException exception) { documentSpec = null; // test HResult for Finding the document specification by message type "..." failed. Verify the schema deployed properly. if (exception.HResult == E_SCHEMA_NOT_FOUND) { return(false); } if (_logger.IsWarnEnabled) { _logger.Warn(string.Format("SafeGetDocumentSpecByType({0}) has failed.", docType), exception); } throw; } }
private string GetSourceMessageMatchingMapName(ArrayList mapList, string btsMsgType, IPipelineContext context) { string mapName = string.Empty; if (mapList.Count > 1) { foreach (string map in mapList) { try { Type type = Type.GetType(map); TransformMetaData transformMetaData = TransformMetaData.For(type); SchemaMetadata sourceSchemaMetadata = transformMetaData.SourceSchemas[0]; string mapSourceSchemaName = sourceSchemaMetadata.SchemaName; IDocumentSpec documentSpec = context.GetDocumentSpecByType(btsMsgType); string msgSourceSchemaName = documentSpec.DocType; Logger.WriteTrace("Map Source Schema Name: " + mapSourceSchemaName); Logger.WriteTrace("Msg Source Schema Name: " + msgSourceSchemaName); if (string.Compare(mapSourceSchemaName, msgSourceSchemaName, false, CultureInfo.CurrentCulture) == 0) { Logger.WriteTrace("Match found: " + map); mapName = map; break; } } catch (Exception) { } } } else { mapName = mapList[0].ToString(); } return(mapName); }
/// <summary> /// Executes the logic for this component. /// </summary> /// <param name="pContext">Pipeline context</param> /// <param name="pInMsg">Input message</param> /// <returns>Outgoing message</returns> private IBaseMessage ExecuteInternal(IPipelineContext pContext, IBaseMessage pInMsg) { // Check arguments if (null == pContext) { throw new ArgumentNullException("pContext"); } if (null == pInMsg) { throw new ArgumentNullException("pInMsg"); } if (null == pInMsg.BodyPart) { throw new ArgumentNullException("pInMsg.BodyPart"); } if (null == pInMsg.BodyPart.GetOriginalDataStream()) { throw new ArgumentNullException("pInMsg.BodyPart.GetOriginalDataStream()"); } // // The logic behind this component is as follows: // 1. Create a seekable read-only stream over the input message body part stream // (because input message can be both large and stream can be non-seekable, // so it should have small memory footprint and change stream positions). // 2. Create a new outgoing message, new body part for it, assign seekable read-only stream // to the new body part, clone body part properties, clone message context. // 3. Get a schema for the input message or based on schemas specified during // design time. // 4. Load stream into XmlDocument. // 5. Walk through promoted properties and distinguished fields and promote/write // them to the message context of the outgoing message. // 6. Return outgoing message. // // // 1. Create a seekable read-only stream over the input message body part stream. // // Create a virtual stream, using GetOriginalDataStream() method on the IBaseMessagePart because // this call doesn't clone stream (instead of IBaseMessagePart.Data property). SeekableReadOnlyStream stream = new SeekableReadOnlyStream(pInMsg.BodyPart.GetOriginalDataStream()); // // 2. Create a new outgoing message, copy all required stuff. // // Create a new output message IBaseMessage outMessage = pContext.GetMessageFactory().CreateMessage(); // Copy message context by reference outMessage.Context = pInMsg.Context; // Create new message body part IBaseMessagePart newBodyPart = pContext.GetMessageFactory().CreateMessagePart(); // Copy body part properties by references. newBodyPart.PartProperties = pInMsg.BodyPart.PartProperties; // Set virtual stream as a data stream for the new message body part newBodyPart.Data = stream; // Copy message parts CopyMessageParts(pInMsg, outMessage, newBodyPart); // // 3. Get a schema for the message. // // Initialize schema map SchemaMap schemaMap = new SchemaMap(this.documentSchemaList); // Get message type from the message data stream string messageType = GetMessageType(stream); // Get a document spec from based on the message type IDocumentSpec documentSpec = schemaMap[messageType]; if (null == documentSpec) { documentSpec = pContext.GetDocumentSpecByType(messageType); } // Promote BTS.MessageType message context property to allow orchestration schedule instances be activated // on produced message. outMessage.Context.Promote(messageTypeWrapper.Name.Name, messageTypeWrapper.Name.Namespace, messageType); // // 4. Load document stream into XmlDocument. // // Save new message stream's current position long position = stream.Position; // Load document into XmlDocument XmlDocument document = new XmlDocument(); document.Load(stream); // Restore the 0 position for the virtual stream Debug.Assert(stream.CanSeek); // Restore new message stream's current position stream.Position = position; // // 5. Walk through promoted properties/distinguished fields and promote/write them. // // Walk through and promote properties IEnumerator annotations = documentSpec.GetPropertyAnnotationEnumerator(); while (annotations.MoveNext()) { IPropertyAnnotation annotation = (IPropertyAnnotation)annotations.Current; XmlNode propertyNode = document.SelectSingleNode(annotation.XPath); if (propertyNode != null) { // Promote context property to the message context as a typed value outMessage.Context.Promote(annotation.Name, annotation.Namespace, promotingMap.MapValue(propertyNode.InnerText, annotation.XSDType)); } } // Walk through and write distinguished fields IDictionaryEnumerator distFields = documentSpec.GetDistinguishedPropertyAnnotationEnumerator(); // IDocumentSpec.GetDistinguishedPropertyAnnotationEnumerator() can return null if (distFields != null) { while (distFields.MoveNext()) { DistinguishedFieldDefinition distField = (DistinguishedFieldDefinition)distFields.Value; XmlNode distFieldNode = document.SelectSingleNode(distField.XPath); if (distFieldNode != null) { // Write distinguished field to the message context as a string value outMessage.Context.Write(distField.XPath, Globals.DistinguishedFieldsNamespace, distFieldNode.InnerText); } } } // // 6. Return outgoing message. // return(outMessage); }
/// <summary> /// processes the inbound message part /// </summary> /// <param name="pc"></param> /// <param name="inmsg"></param> /// <param name="outmsg"></param> /// <param name="part"></param> private void ProcessPart(IPipelineContext pc, IBaseMessage inmsg, IBaseMessage outmsg, IBaseMessagePart part) { IDocumentSpec docSpec = null; Stream dataStream = part.GetOriginalDataStream(); MarkableForwardOnlyEventingReadStream eventingDataStream = new MarkableForwardOnlyEventingReadStream(dataStream); XmlSchemaCollection schemaCollection = new XmlSchemaCollection(new NameTable()); schemaCollection.ValidationEventHandler += new ValidationEventHandler(this.ValidationCallBack); // retrieve the assigned document schemas to validate against SchemaList docSchemas = this.DocumentSchemas; // retrieve the namespace this document adheres to string contextProperty = (string)outmsg.Context.Read(XmlCompleteValidator._documentSpecNameProperty.Name.Name, XmlCompleteValidator._documentSpecNameProperty.Name.Namespace); // if the inbound message has a namespace, if (contextProperty != null && contextProperty.Length > 0) { // clear the original schemas to validate against docSchemas.Clear(); string[] contextSchemas = contextProperty.Split(new char[] { '|' }); // set it's schemas foreach (string schemaName in contextSchemas) { docSchemas.Add(new Schema(schemaName)); } } #region retrieve validation schemas, shamelessly copied from the original XmlValidator pipeline component bool validateSchemas = this.DocumentSchemas != null && this.DocumentSchemas.Count > 0; if (validateSchemas && this.DocumentSchemas.Count == 1 && this.DocumentSchemas[0].SchemaName.Length == 0) { validateSchemas = false; } if (validateSchemas) { foreach (Schema s in docSchemas) { try { docSpec = pc.GetDocumentSpecByName(s.SchemaName); } catch (COMException e) { throw new XmlCompleteValidatorException( ExceptionType.CANNOT_GET_DOCSPEC_BY_NAME, e.ErrorCode.ToString("X") + ": " + e.Message, new string[] { s.SchemaName }); } if (docSpec == null) { throw new XmlCompleteValidatorException( ExceptionType.CANNOT_GET_DOCSPEC_BY_NAME, string.Empty, new string[] { s.SchemaName }); } XmlSchemaCollection coll = docSpec.GetSchemaCollection(); schemaCollection.Add(coll); } } else { try { docSpec = pc.GetDocumentSpecByType(Utils.GetDocType(eventingDataStream)); } catch (COMException e) { throw new XmlCompleteValidatorException( ExceptionType.CANNOT_GET_DOCSPEC_BY_TYPE, e.ErrorCode.ToString("X") + ": " + e.Message, new string[] { Utils.GetDocType(eventingDataStream) }); } if (docSpec == null) { throw new XmlCompleteValidatorException( ExceptionType.CANNOT_GET_DOCSPEC_BY_TYPE, string.Empty, new string[] { Utils.GetDocType(eventingDataStream) }); } schemaCollection = docSpec.GetSchemaCollection(); } #endregion // the most critical line within this component, assign an // XmlEventingValidationStream to ensure the inbound messagestream is validated // and events can be assigned which allow us to capture any erros that might occur XmlEventingValidationStream validatingStream = new XmlEventingValidationStream(eventingDataStream); // add the schemas we'd like to validate the inbound message against validatingStream.Schemas.Add(schemaCollection); // assign a validation event which will accumulate any errors within the inbound message validatingStream.ValidatingReader.ValidationEventHandler += new ValidationEventHandler(XmlMessageValidationCallBack); // and assign the AfterLastReadEvent, which fires upon reading the last piece of information // from the inbound message stream and pushes all accumulated error information out into // the eventviewer and onto the HAT context by throwing an exception which contains the errors validatingStream.AfterLastReadEvent += new AfterLastReadEventHandler(validatingStream_AfterLastReadEvent); // duplicate the inbound message part by creating a new one and copying it's properties IBaseMessageFactory messageFactory = pc.GetMessageFactory(); IBaseMessagePart messagePart = messageFactory.CreateMessagePart(); // if the inbound message exists and has a body part, copy the part properties // into the outbound messagepart if (inmsg != null && inmsg.BodyPart != null) { messagePart.PartProperties = PipelineUtil.CopyPropertyBag(inmsg.BodyPart.PartProperties, messageFactory); } // set the outbound charset messagePart.Charset = "UTF-8"; // set the outbound content type messagePart.ContentType = "text/xml"; // and assign the outbound datastream messagePart.Data = validatingStream; // finally, copy existing message parts CopyMessageParts(pc, inmsg, outmsg, messagePart, false); }
public bool Probe(IPipelineContext pContext, IBaseMessage pInMsg) { // Check arguments if (null == pContext) { throw new ArgumentNullException("pContext"); } if (null == pInMsg) { throw new ArgumentNullException("pInMsg"); } // We need to determine a document schema to use based on message content. For the sake of simplicity of this // sample, we will check the first two characters in input stream and map them to some schema message types we // have predefined. The more sofisticated component could use UI configuration options to map identification // text located at specified offsets in message stream and having specified length, which could map to specified // message type or document spec type name. // Check whether input message doesn't have a body part or it is set to null, fail probe in those cases if (null == pInMsg.BodyPart || null == pInMsg.BodyPart.GetOriginalDataStream()) { return(false); } SeekableReadOnlyStream stream = new SeekableReadOnlyStream(pInMsg.BodyPart.GetOriginalDataStream()); Stream sourceStream = pInMsg.BodyPart.GetOriginalDataStream(); // Check if source stream can seek if (!sourceStream.CanSeek) { // Create a virtual (seekable) stream SeekableReadOnlyStream seekableStream = new SeekableReadOnlyStream(sourceStream); // Set new stream for the body part data of the input message. This new stream will then used for further processing. // We need to do this because input stream may not support seeking, so we wrap it with a seekable stream. pInMsg.BodyPart.Data = seekableStream; // Replace sourceStream with a new seekable stream wrapper sourceStream = pInMsg.BodyPart.Data; } // Preserve the stream position long position = sourceStream.Position; char [] signature = new char[2]; try { // Read signature from a stream StreamReader reader = new StreamReader(sourceStream); if (reader.Read(signature, 0, signature.Length) < signature.Length) { return(false); } // Don't close stream reader to avoid closing of underlying stream } finally { // Restore the stream position sourceStream.Position = position; } // Get message type from signature string messageType = GetMessageType(new string(signature)); // Fail if message type is unknown if (null == messageType) { return(false); } // Get document spec from the message type IDocumentSpec documentSpec = pContext.GetDocumentSpecByType(messageType); // Instead of loading schema to get a document spec type name we could change implementation to return defined // during a design time document spec type name and directly specify it in the call below: // Write document spec type name to the message context so Flat File disassembler could access this property and // do message processing for a schema which has document spec type name we've discovered pInMsg.Context.Write(DocumentSpecNamePropertyName, XmlNormNamespaceURI, documentSpec.DocSpecStrongName); // Delegate call to Flat File disassembler return(disassembler.Probe(pContext, pInMsg)); }
/// <summary> /// Execute the Custom Logic To Do the Schema Validation /// </summary> /// <param name="pContext">Pipeline context</param> /// <param name="pInMsg">Input message to the pipeline</param> /// <returns>Output message</returns> public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg) { _logger.Debug("PipelineComponent::XmlValidator: Executing validation pipeline component."); int maxErrorCount = 20; //Parse the maxErrorCount property value to int Int32.TryParse(_maxErrorCount, out maxErrorCount); string messageType = Convert.ToString(pInMsg.Context.Read(Constants.MessageTypePropName, Constants.SystemPropertiesNamespace)); string messageId = Convert.ToString(pInMsg.MessageID); string fileName = Convert.ToString(pInMsg.Context.Read(Constants.ReceivedFileNamePropName, Constants.FileAdapterPropertiesNameSpace)); fileName = PipelineHelper.GetFileNameWithoutExtension(fileName); XmlValidatorHelper helper = new XmlValidatorHelper(); // (maxErrorCount, fileName); helper.Logger = _logger; if (pContext == null) { throw new ArgumentNullException("Pipeline Context is null"); } if (pInMsg == null) { throw new ArgumentNullException("Incoming Message in null"); } //Create OutMessage //Invoke the XMLValidator Validate Method var originalStream = pInMsg.BodyPart.GetOriginalDataStream(); Stream seekableStream; if (!originalStream.CanSeek) { seekableStream = new ReadOnlySeekableStream(originalStream); pInMsg.BodyPart.Data = seekableStream; } else { seekableStream = originalStream; } IDocumentSpec docSpec = null; try { docSpec = pContext.GetDocumentSpecByType(messageType); //Use a temperory Disposable Virtual Stream using (VirtualStream virtusalStream = new VirtualStream()) { seekableStream.CopyTo(virtusalStream); virtusalStream.Position = 0; helper.ValidationWrapper(docSpec, maxErrorCount, virtusalStream, messageType, messageId); } //var errorProcessStatus = null; // helper.ProcessStatus; ////Check if validation Helper has caught any errors //if (errorProcessStatus.Errors != null && errorProcessStatus.Errors.Count() > 0) //{ // return CreateOutputMessageWrapper(pContext, pInMsg, errorProcessStatus, fileName); //} //else //{ // //Need to move the wrapped seekable stream to beginning and assign to the BodyPart as incoming message is not seekable // seekableStream.Seek(0, SeekOrigin.Begin); // // Track the stream so that it can be disposed when the message is finially finished with // pContext.ResourceTracker.AddResource(pInMsg.BodyPart.Data); return(pInMsg); //} } catch (Exception ex) { string errorMessage = string.Format("RequestId: {0} ErrorMessage: {1}", fileName, ex.Message); _logger.Error(errorMessage); return(pInMsg); } }
/// <summary> /// Updates the BizTalk BaseMessage and Message Context with any new or modified values from the executed BRE Policies. /// </summary> /// <param name="pipelineContext">PipelineContext</param> /// <param name="baseMessage">BizTalk BaseMessage to update</param> /// <param name="allowRepromotion">Value indicating whether to allow to re-promote properties.</param> public void ApplyMessageContextUpdates(IPipelineContext pipelineContext, IBaseMessage baseMessage, bool allowRepromotion) { try { if (baseMessage == null || this.UpdateMessageContext == false) { return; } foreach (var keyValuePair in this.contextProperties) { ContextProperty prop = keyValuePair.Value; // update the message context with the context properties that // have been changed (dirty) if (prop.Dirty == true) { // check to determine if we should promote if (prop.Promote == true) { // however check to see if already promoted or not bool isAlreadyPromoted = false; var ovalue = baseMessage.Context.Read(prop.Name, prop.Namespace); if (ovalue != null) { isAlreadyPromoted = baseMessage.Context.IsPromoted(prop.Name, prop.Namespace); } if (isAlreadyPromoted == true) { // we need to remove and re - promote baseMessage.Context.Write(prop.Name, prop.Namespace, null); baseMessage.Context.Promote(prop.Name, prop.Namespace, null); baseMessage.Context.Promote(prop.Name, prop.Namespace, prop.Value); } else { // it's not already promoted and we should promote if we can, // this assumes there is a valid property schema, name, and data type associated with it for promotion validation... // dangerous operation which could cause cyclic loop by re-promoting a property that was slated to be demoted *wasPromote*... if (allowRepromotion == true) { try { baseMessage.Context.Write(prop.Name, prop.Namespace, null); baseMessage.Context.Promote(prop.Name, prop.Namespace, null); baseMessage.Context.Promote(prop.Name, prop.Namespace, prop.Value); } catch (Exception ex) { TraceProvider.Logger.TraceError("Context property: {0}#{1} caused an exception:\n{2}\nThis property was not promoted.", prop.Namespace, prop.Name, ex.Message); } } } } else { // we don't need to promote it, only write it (Distinguished) // we need to remove and re-write it baseMessage.Context.Write(prop.Name, prop.Namespace, null); baseMessage.Context.Write(prop.Name, prop.Namespace, prop.Value); } } } if (this.AutoSetDocumentSpecName == true) { // set document spec name based on the message type string messageType = baseMessage.Context.Read <string>(BtsProperties.MessageType.Name, BtsProperties.MessageType.Namespace, null); if (string.IsNullOrEmpty(messageType) == false) { IDocumentSpec documentSpec = pipelineContext.GetDocumentSpecByType(messageType); TraceProvider.Logger.TraceInfo("Using document specification: {0}", documentSpec.DocSpecName); // write document spec type name to the message context so that the Flat File disassembler could access this // property and do the message processing for a schema that has the document spec type name we've discovered baseMessage.Context.Write(BtsProperties.DocumentSpecName.Name, BtsProperties.DocumentSpecName.Namespace, documentSpec.DocSpecStrongName); } } pipelineContext.ResourceTracker.AddResource(baseMessage.Context); } catch (Exception ex) { TraceProvider.Logger.TraceError(ex); throw; } }