BizTalk - Fail to Promote Properties - biztalk

Using BizTalk 2013r2 CU1, I have a created a property schema for my inbound xsd and deployed the application.
When I receive a sample xml document using a standard "xml receive" pipeline then I can see that the required element is promoted into the context as expected.
I then created a custom pipeline which contains the "XML disassembler" component in the "Disassemble" stage and a custom component in the "Validate" stage. This custom component needs to read the promoted property from the context. However, I find that when I switch the Receive Location from "xml receive" pipeline to my custom pipeline then my property does not get promoted. I am using the following code within my custom component to write out a list of items in the message context:
for (int x = 0; x < contextList.CountProperties; x++)
{
contextList.ReadAt(x, out name, out nspace);
string value = contextList.Read(name, nspace).ToString();
contextItems += "Name: " + name + " - " + "Namespace: " + nspace + " - " + value + "\r\n";
if (name == _ContextPropertyName && nspace == _ContextPropertyNamespace)
promotedPropFound = true;
}
Helpers.EventLogHelper eventHelper = new EventLogHelper();
eventHelper.LogEvent(string.Format("Context items:{0}", contextItems));
if (promotedPropFound == false)
throw new Exception(string.Format("Unable to find promoted property with name[{0}] and namespace [{1}]", _ContextPropertyName, _ContextPropertyNamespace));
From the output in the event log I can see that certain properties such as MessageType have been promoted but my custom property has not. Again, if I change the receive location back to use a standard "xml receive" pipeline then the property will be promoted from a copy of the same xml document (I check this by stopping the subscribing send port and viewing the context from the admin console).
I find this very strange since the same "XML disassembler" component is present in the same "Disassemble" stage of both pipelines, with the same (default)configuration. I'm starting to think perhaps there's a problem with 2013r2CU1 - has anyone else encountered the same?

By the time the XML Disassembler has executed in your custom pipeline, there is no guarantee that your properties have been promoted.
The incoming message arrives in the pipeline as a stream with the data pointer set at the start of the stream.
I think the XML Disassembler does not read the stream, it wraps it into some stream wrapper class that will populate the promoted properties when the stream actually gets read.
The stream will have to be read at least once: when the message gets inserted into the message box. So there is a guarantee that the properties will get promoted, but you cannot assume it will be done before the "Validate" stage executes.
To make sure this is really the problem your are encountering: check your message AFTER it has been imported into the message box.
If your promoted property is there, what I described is probably what is happening.
Solutions:
To make your custom pipeline component work, the best solution would be to do just as the XML Disassembler: get the incoming stream and wrap it into a stream wrapper class that can trigger whatever functionality you need.
The assembly Microsoft.BizTalk.Streaming.dll has some wrapper class that might interest you: ForwardOnlyEventingReadStream.
This class has an event AfterLastReadEvent. You can create some EventHandler and have it subscribe to this event to trigger your custom functionality only after the stream has been fully read., and all properties have been promoted.
Your custom component would look like that:
public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
Stream stream = message.BodyPart.GetOriginalDataStream();
CForwardOnlyEventingReadStream eventingReadStream = new CForwardOnlyEventingReadStream(stream);
eventingReadStream.AfterLastReadEvent += new AfterLastReadEventHandler(DoSomething);
message.BodyPart.Data = eventingReadStream;
return message;
}
private static void DoSomething(object src, EventArgs args)
{
}
A less efficient way to solve your problem would be to read the stream fully in your custom component at the "Validate" stage and put the stream pointer back to the start of the stream.
Microsoft has some guidelines for when you're manipulating the message stream in pipeline component:
https://msdn.microsoft.com/en-us/library/aa577699.aspx
Update:
OP needs to pass the message context to the Event Handler.
It is possible using a Lambda expression:
public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
Stream stream = message.BodyPart.GetOriginalDataStream();
CForwardOnlyEventingReadStream eventingReadStream = new CForwardOnlyEventingReadStream(stream);
eventingReadStream.AfterLastReadEvent += new AfterLastReadEventHandler((src, args) => DoSomething(src, args, message.Context));
message.BodyPart.Data = eventingReadStream;
return message;
}
private static void DoSomething(object src, EventArgs args, IBaseMessageContext messageContext)
{
}
This SO question can be interesting for reference for passing the additional parameter:
Pass parameter to EventHandler

Can you do whatever you had planned for the Validate Stage in an Orchestration? That would be much easier.
If not, the most common solution to this specific problem is an intermediate Pipeline Component that forces a full read on the stream, though technically, you'd only have to read until the Promoted node is hit.

Related

faild to build body in activemq

I want use ActiveMQ in .net core,i use Apache.NMS.ActiveMQ for doing this but I have problem.
I see this error in ActiveMQ admin console:
Cannot display ObjectMessage body. Reason: Failed to build body from bytes. Reason: java.io.StreamCorruptedException: invalid stream header: 00010000
thats part of my code for doing this:
private const String QUEUE_DESTINATION = "test-queue";
private IConnection _connection;
private ISession _session;
public MessageQueue()
{
IConnectionFactory factory = new ConnectionFactory("tcp://localhost:61616?wireFormat.maxInactivityDuration=5000000");
_connection = factory.CreateConnection();
_connection.Start();
_session = _connection.CreateSession();
}
IDestination dest = _session.GetQueue(QUEUE_DESTINATION);
using (IMessageProducer producer = _session.CreateProducer(dest))
{
var objectMessage = producer.CreateObjectMessage(newDoc);
producer.Send(objectMessage);
}
The fact that the admin console can't display the body of an ObjectMessage isn't really an error. It's the expected behavior. Remember, from the broker's perspective the message body is just an array of bytes. It could be text data (encoded many different ways), image data, custom binary data, etc. The broker has no idea how to decode it. It will try to display the body as text, but if it fails it won't try anything else.
To be clear, in order to see the contents of an ObjectMessage the web console would have to have the object's definition in order to deserialize it. There is no mechanism to tell the web console about arbitrary data formats so it can deserialize message bodies reliably (other than simple text). This is one reason, among many, to avoid ObjectMessage.
I recommend you use a simple text format (e.g. JSON, XML) to represent your data and send that in your message rather than using ObjectMessage.

Servlet Context, Collections and Serialization

Often, I work on Java EE application. Today I'm facing an issue: serialize Collections in servlet context. In my case, my app contains a Servlet Context Listener and many servlets.
The context listener load a ConcurrentHashMap containing several lists of products at initialisation and a task scheduler to refresh this list.
The servlets are supposed to access the right list, based on user provided parameters.
Here the code of my contextInitialized Listener:
public void contextInitialized(ServletContextEvent event) {
app = event.getServletContext();
myMap = new ConcurrentHashMap<String, Catalog>();
myMap.put("FR", new Catalog());
myMap.put("UK", new Catalog());
app.setAttribute("catalogue", myMap);
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new AutomateRefresh(), 0, 60, TimeUnit.MINUTES);
}
In order to show my problem, i created a servlet that display everything which is a boolean or a ConcurrentHashMap in context
I'm not surprised to find this kind of results:
javax.servlet.context.tempdir is equal to...
Working is equal to... true
org.apache.catalina.resources is equal to...
org.apache.tomcat.InstanceManager is equal to...
org.apache.catalina.jsp_classpath is equal to...
javax.websocket.server.ServerContainer is equal to...
org.apache.jasper.compiler.TldCache is equal to...
catalogue is equal to...
org.apache.tomcat.JarScanner is equal to...
As you can see, my two custom keys (the boolean Working and the ConcurrentHashMap catalogue) exists. But catalogue is empty when not accessed inside the Listener.
I found that:
The serialization form of java.util.HashMap doesn't serialize the buckets themselves, and the hash code is not part of the persisted state.
Source: Serializing and deserializing a map with key as string
For many projects a serializable and thread-safe collection is useful. I am probably not the only one who is looking for that (see the amount of topic about servlet context).
ConcurrentHashMap is thread-safe but I am unable to retrieve my data in other servlet (in the same app). Is there an implementation of Collection which is thread-safe and serializable (due to WebLogic server policy) ? Or am I using it in a wrong way ?
EDIT: Code of "Display context servlet"
public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException{
System.out.println("List of all values in the context:");
Enumeration<?> e = getServletContext().getAttributeNames();
while (e.hasMoreElements())
{
String name = (String) e.nextElement();
System.out.print("\n" + name + " is equal to... ");
// Get the value of the attribute
Object value = this.getServletContext().getAttribute(name);
if (value instanceof ConcurrentHashMap) {
ConcurrentHashMap<String, Catalog> map = (ConcurrentHashMap<String, Catalog>) value;
Iterator<Entry<String, Catalog>> it = map.entrySet().iterator();
while (it.hasNext()) {
ConcurrentHashMap.Entry<String, Catalog> entry = (ConcurrentHashMap.Entry<String, Catalog>)it.next();
System.out.print("\t" + entry.getKey() + "=" + entry.getValue());
}
} else if (value instanceof Boolean) {
System.out.print((Boolean)value);
}
}
}
EDIT2: Like BalusC suggested the HashMap maybe null (a rookie mistake ?).
Here the task code. The task is in the Listener. The Listener initialize the HashMap with new empty object. The task refresh the objects when webapp starts and then every hour.
public class AutomateRefresh implements Runnable {
public void run() {
System.out.println("Scheduler trigger");
if(app.getAttribute("catalogue") instanceof ConcurrentHashMap){
myMap = (ConcurrentHashMap<String, Catalog>) app.getAttribute("catalogue");
//Autorefresh
Iterator<Entry<String, Catalog>> it = myMap.entrySet().iterator();
while (it.hasNext()) {
ConcurrentHashMap.Entry<String, Catalog> entry = (ConcurrentHashMap.Entry<String, Catalog>)it.next();
((Catalog)entry.getValue()).setValid(false);//Set as not valid anymore for further request
try {
((Catalog)entry.getValue()).refreshdb((String) entry.getKey());//TODO rework to use REST API
} catch (SQLException e) {
e.printStackTrace();
}
it.remove(); // avoids a ConcurrentModificationException
app.setAttribute("catalogue", myMap);
app.setAttribute("Working", true);
System.out.println((String)entry.getKey() + " = " + (Catalog)entry.getValue());
}
}
else{
System.out.println("Catalogue is not an instance of ConcurrentHashMap as expected.");
app.setAttribute("Working", false);
}
}
}
When the task triggered, for each Catalog stored in the Context, the task update the data stored by them. It also display data in console.
Results:
Refresh Catalog for UK with DB
UK = Catalog [list size is : 0 valid=true, lastToken=notoken]
Refresh Catalog for FR with DB
FR = Catalog [list size is : 30 valid=true, lastToken=notoken]
Catalog is a class with an ArrayList, a boolean and a String. Everything seems correct: UK is supposed to be empty but not null and FR is supposed to contains 30 products.
I still can not access this data in other servlets.
I found the origin of the problem, a rookie mistake as expected:
I tried to update this way, assuming it would have updated the object directly in the ConcurrentHashMap
((Catalog)entry.getValue()).refreshdb((String) entry.getKey());
I replace it by:
Catalog myCatalog = (Catalog)entry.getValue();
myCatalog.refreshdb((String) entry.getKey());
myMap.put((String)entry.getKey(), myCatalog);
And it works now.
I still don't know why my objects were accessible from the listener, they are not supposed to work that way. Maybe a strange behavior from my server ? Anyway, this issue is fixed.
Thanks to BalusC for his help.

Referencing a port in an orchestration via a string variable

I am attempting to develop a generic BizTalk application for configuring dynamic ports. I have an orchestration that pulls back all the configuration settings for each port and I want to loop through these settings and configure the ports. The settings are held in MSSQL and, for instance, two of the properties are PortName and Address. So from within the orchestration I would like to reference the port by the string variable PortName. So is there some way to get a collection of all the ports in an orchestration or reference a port via a string variable i.e. Port['MyPortName'](Microsoft.XLANGs.BaseTypes.Address) = "file://c:\test\out\%MessageId%.xml" Thanks
In order to dynamically configure Dynamic Logical Send Ports from within an orchestration, one has to store the settings into a persistent datastore (e.g. a database or configuration file) and implement a way to assign those properties dynamically at runtime.
But first, we need to understand what is happening when configurating a Dynamic Send Port.
How to Configure a Dynamic Logical Send Port
Configuring the properties of a dynamic logical send port from within an orchestration involves two steps:
First, the TransportType and target Address properties must be specified on the Send Port. This is usually done in an Expression Shape with code similar to this:
DynamicSendPort(Microsoft.XLANGs.BaseTypes.TransportType) = "FILE";
DynamicSendPort(Microsoft.XLANGs.BaseTypes.Address) = "C:\Temp\Folder\%SourceFileName%";
Second, any additional transport properties must be specified on the context of the outgoing message itself. Virtually all BizTalk adapters have additional properties that are used for the communication between the Messaging Engine and the XLANG/s Orchestration Engine. For instance, the ReceivedFileName context property is used to dynamically set a specific name for when the FILE adapter will save the outgoing message at its target location. This is best performed inside an Assignment Shape, as part of constructing the outgoing message:
OutgoingMessage(FILE.ReceiveFileName) = "HardCodedFileName.xml"
You'll notice that most configuration properties must be specified on the context of the outgoing messages, specifying a namespace prefix (e.g. FILE), a property name (e.g. ReceiveFileName) and, obviously, the value that gets assigned to the corresponding property.
In fact, all the context properties are classes that live Inside the well-known Microsoft.BizTalk.GlobalPropertySchemas.dll assembly. This is confirmed by looking up this assembly in Visual Studio's object explorer.
Even though most context properties that are necessary to configure Dynamic Logical Send Ports live Inside this specific assembly, not all of them do. For instance, the MSMQ BizTalk adapter uses a separate assembly to store its context properties. Obviously, third-party or custom adapters come with additionnal assemblies as well.
Therefore, in order to setup a context property on a Dynamic Send Port using a flexible approach like the one describe below, four pieces of information are necessary:
The fully qualified name of the assembly containing the context property classes.
The namespace prefix.
The property name.
The property value.
Storing Port Settings in a Persistent Medium
The following .XSD schema illustrate one possible structure for serializing port settings.
Once serialized, the specified context properties can then be stored in a SQL database or a configuration file very easily. For instance, here are the settings used as an example in this post:
A Flexible Approach to Configuring Dynamic Logical Send Ports
With a simple helper Library, setting up the dynamic port configuration is very easy. First, you have to retrieve the serialized settings from the persistent medium. This can easily be achieved using the WCF-SQL Adapter and a simple stored procedure.
Once retrieved, those properties can then be deserialized into a strongly-typed C# object graph. For this, first create a C# representation of the ContextProperties schema shown above, using the following command-line utility:
xsd.exe /classes /language:cs /namespace:Helper.Schemas .\ContextProperties.xsd
This generates a partial class that can be improved with the following method:
namespace Helper.Schemas
{
public partial class ContextProperties
{
public static ContextProperties Deserialize(string text)
{
using (MemoryStream stream = new MemoryStream())
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
stream.Write(buffer, 0, buffer.Length);
stream.Seek(0, SeekOrigin.Begin);
return (ContextProperties)
Deserialize(
stream
, typeof(ContextProperties));
}
}
public static Object Deserialize(Stream stream, Type type)
{
XmlSerializer xmlSerializer = new XmlSerializer(type);
return xmlSerializer.Deserialize(stream);
}
}
}
Second, applying this configuration involves creating an XLANG/s message from code and setting up the context properties dynamically using reflection, based upon the description of the context property classes specified in the deserialized ContextProperties object graph.
For this, I use a technique borrowed from Paolo Salvatori's series of articles regarding dynamic transformations, which consists in creating a custom BTXMessage-derived class, used internally by the BizTalk XLANG/s engine.
namespace Helper.Schemas
{
using Microsoft.BizTalk.XLANGs.BTXEngine; // Found in Microsoft.XLANGs.BizTalk.Engine
using Microsoft.XLANGs.Core; // Found in Microsoft.XLANGs.Engine
[Serializable]
public sealed class CustomBTXMessage : BTXMessage
{
public CustomBTXMessage(string messageName, Context context)
: base(messageName, context)
{
context.RefMessage(this);
}
public void SetContextProperty(string assembly, string ns, string name, object value)
{
if (String.IsNullOrEmpty(ns))
ns = "Microsoft.XLANGs.BaseTypes";
if (String.IsNullOrEmpty(assembly))
assembly = "Microsoft.BizTalk.GlobalPropertySchemas";
StringBuilder assemblyQualifiedName = new StringBuilder();
assemblyQualifiedName.AppendFormat("{0}.{1}, {2}", ns, name, assembly);
Type type = Type.GetType(assemblyQualifiedName.ToString(), true, true);
SetContextProperty(type, value);
}
internal void SetContextProperty(string property, object value)
{
int index = property.IndexOf('.');
if (index != -1)
SetContextProperty(String.Empty, property.Substring(0, index), property.Substring(index + 1), value);
else
SetContextProperty(String.Empty, String.Empty, property, value);
}
}
}
Now, the last piece of the puzzle is how to make use of this custom class from within an Orchestration. This is easily done in an Assignment Shape using the following helper code:
namespace Helper.Schemas
{
using Microsoft.XLANGs.BaseTypes;
using Microsoft.XLANGs.Core; // Found in Microsoft.XLANGs.Engine
public static class Message
{
public static XLANGMessage SetContext(XLANGMessage message, ContextProperties properties)
{
try
{
// create a new XLANGMessage
CustomBTXMessage customBTXMessage = new CustomBTXMessage(message.Name, Service.RootService.XlangStore.OwningContext);
// add parts of the original message to it
for (int index = 0; index < message.Count; index++)
customBTXMessage.AddPart(message[index]);
// set the specified context properties
foreach (ContextPropertiesContextProperty property in properties.ContextProperty)
customBTXMessage.SetContextProperty(property.assembly, property.#namespace, property.name, property.Value);
return customBTXMessage.GetMessageWrapperForUserCode();
}
finally
{
message.Dispose();
}
}
}
}
You can use this static method inside your Assignment Shape like the code shown hereafter, where OutboundMessage represents the message which you want to set the context:
OutboundMessage = Helper.Schemas.Message.SetContext(OutboundMessage, contextProperties);
In the first place you shouldn't attempt to do configuration changes like this using an Orchestration. Technically it's feasible to do what you are attempting to do, but as a practice you shouldn't mix up your business process with administration.
The best way to do such things will be by either writing some normal scripts or PowerShell.
To answer you question, you can get the data you want from BtsOrchestration class in ExplorerOM
http://msdn.microsoft.com/en-us/library/microsoft.biztalk.explorerom.btsorchestration_members(v=bts.20)

WCF call via MSMQ erratic

The code is trivial so rather than explain, here's the relevant portion:
protected void Page_Load(object sender, EventArgs e)
{
if ((Request.HttpMethod == "POST") && (Request.ContentType == "text/xml"))
{
string filename = string.Format("{0:yyyyMMddHHmmss}.xml", DateTime.UtcNow);
var path = Path.Combine(Request.MapPath("Chapters"), Request.Headers["X-MAC"]);
Directory.CreateDirectory(path);
string xml;
using (ChapterWriterClient client = new ChapterWriterClient())
using (StreamReader sr = new StreamReader(Request.InputStream))
{
xml = sr.ReadToEnd();
client.Write(xml);
System.Diagnostics.Trace.TraceInformation(xml);
}
Request.SaveAs(Path.Combine(path, filename), false);
}
There's more but the rest of it works as expected.
Here's the problem: the call client.Write(string xml) is erratic in whether it succeeds.
Some known facts:
No error is ever reported to the debugger or the event log
Sometimes the XML makes it into the message queue and sometimes it doesn't
Request.SaveAs(string filepath, bool includeHeaders) call always succeeds and the resultant file always contains the expected XML. This has several important implications:
The call to WCF is being executed.
Because the string xml had the expected value immediately after the failed WCF call, and assignment to this variable occurs only prior to the WCF call, it must have had the correct value at the time of the call.
There is some variation in message size so I'm checking the service config in that regard but past experience tells me to expect an exception thrown when the payload is too big.
Any clues would be appreciated.
Also, as you can probably see the whole point of the exercise is a somewhat naiive attempt to use an HTTP POST to put some XML into a message on MSMQ. I'd be perfectly happy with a "Don't do it like that, do it like this you ignorant savage" type answer.

TypeError: Error #1034: Type Coercion failed: cannot convert Object#1456c7b9 to mx.messaging.messages.IMessage

Im trying to connect a Flash client to BlazeDS. There has been some success with this from others using the vanilla BlazeDS setup. However I'm using the new Spring BlazeDS Integration from springsource and running aground.
The flash client actually seems to be working in that I can see the correct data in the body of the returned object, but for some reason unknown it fails casting as an IMessage. It fails in PollingChannel.as on this line with the subject line error
var messageList:Array = msg.body as Array;
for each (var message:IMessage in messageList) <--
On application load I register a whole bunch of classes like so
registerClassAlias( "flex.messaging.messages.RemotingMessage", RemotingMessage );
registerClassAlias("mx.messaging.messages.IMessage", IMessage);
etc..
my code is basically
var channelSet:mx.messaging.ChannelSet = new mx.messaging.ChannelSet();
var channel:mx.messaging.channels.AMFChannel = new AMFChannel("my-amf", "http://localhost:8400/SpringA/messagebroker/amf");
channelSet.addChannel(channel);
var consumer:mx.messaging.Consumer = new Consumer();
consumer.channelSet = channelSet;
consumer.destination = "simple-feed";
consumer.subscribe();
consumer.addEventListener(MessageEvent.MESSAGE, test);
private function test(event:IMessage)
{
trace("msg..");
// breakpoint never makes it here
}
I have a flex client which works 100% with same destination/channel.
The error in the title means that you, for some reason, got an object that is not implementing or extending the IMessage interface, therefore the loop can not cast it in this part:
for each (var message:IMessage in messageList){
Either you should somehow make sure that you don't add anything that is not extending or implementing IMessage, or check if the variable IS actually ext./imp. it. Also - if you want to do that, you will have to change the for each like this:
for each (var obj in messageList){
if (obj is IMessage){
var message:IMessage = obj as IMessage;
// DO STUFF HERE
}
}
Add this Object mapping:
registerClassAlias("flex.messaging.io.ObjectProxy", ObjectProxy);
If on your Java VO objects you have overridden the hashcode() method, this situation could happen.
Remove the hashcode() override (if you are able to).
See my blog for the backstory on how I discovered this. http://squaredi.blogspot.com/2013/12/remoting-landmine-without-stack-trace.html
I had the same error when trying to send an actionscript object to the backend. My problem was that my c# equivalent object was missing an public parameterless constructor.

Resources