Dev Blog - Johan Danforth
I'm Johan Danforth and this is my dev blog - a mix of .NET, ASP.NET, Rest, Azure and some other random coding stuff.
-
Praise to Reflector, I found a nice StackTraceToString() method
I must give my praise to the Reflector program, made by Lutz Roeder. I used it to dig down into the depths of TraceListener, to see how the config sections was read and how the DefaultTraceListener wrote it's stacktrace to the file - StackTraceToString(). Saved me some time, it did ;)
Here's my refactored version:
/// <summary>
/// Converts a stacktrace into something more readable
/// </summary>
/// <param name="trace">the stacktrace to handle</param>
/// <returns>a string with the stacktrace</returns>
public static string StackTraceToString(StackTrace trace)
{
int startFrameIndex = 0;
int endFrameIndex = trace.FrameCount - 1;
int iFrame;
int iParam;
int iFileLineNumber;
StringBuilder sbReturnString;
StackFrame frame;
MethodBase mBase;
ParameterInfo[] arrParamInfo;
ParameterInfo paramInfo;
sbReturnString = new StringBuilder(512);
for (iFrame = startFrameIndex; (iFrame <= endFrameIndex); iFrame = (iFrame + 1))
{
frame = trace.GetFrame(iFrame);
mBase = frame.GetMethod();
sbReturnString.Append(" at ");
sbReturnString.Append(mBase.ReflectedType.Name);
sbReturnString.Append(".");
sbReturnString.Append(mBase.Name);
sbReturnString.Append("(");
arrParamInfo = mBase.GetParameters();
for (iParam = 0; (iParam < arrParamInfo.Length); iParam = (iParam + 1))
{
paramInfo = arrParamInfo[iParam];
if (iParam > 0)
{
sbReturnString.Append(", ");
}
sbReturnString.Append(paramInfo.ParameterType.Name);
sbReturnString.Append(" ");
sbReturnString.Append(paramInfo.Name);
}
sbReturnString.Append(") ");
sbReturnString.Append(frame.GetFileName());
iFileLineNumber = frame.GetFileLineNumber();
if (iFileLineNumber > 0)
{
sbReturnString.Append("(");
sbReturnString.Append(iFileLineNumber.ToString());
sbReturnString.Append(")");
}
sbReturnString.Append(Environment.NewLine);
}
sbReturnString.Append(" ");
return sbReturnString.ToString();
}
}
Thank you, thank you, thank you Lutz :)
-
A dive into the TraceListener class
I had a quick dive into the TraceListener class today. I want to use Assert() in the System.Diagnostics.Trace class from an ASP.NET application, and wondered how to configure the output to file, event log and mail. Making the Assert is easy:
Trace.Assert(errors = 0, "Assert happened!", "A detailed message for you guys!")
Now, to write that to a file, just add to web.config:
<system.diagnostics>
<trace autoflush="false" indentsize="4"/>
<assert assertuienabled="false" logfilename="c:\AssertLog.txt"/>
</system.diagnostics>When an assertion fails, this is written to the file:
---- DEBUG ASSERTION FAILED ----
---- Assert Short Message ----
Assert happened!
---- Assert Long Message ----
A detailed message for you guys!at AssertForm.btnOk_Click(Object sender, EventArgs e) c:\inetpub\wwwroot\AssertTest\AssertForm.aspx.vb(30)
at Button.OnClick(EventArgs e)
at Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument)
at Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
at Page.RaisePostBackEvent(NameValueCollection postData)
at Page.ProcessRequestMain()
at Page.ProcessRequest()
at Page.ProcessRequest(HttpContext context)
at CallHandlerExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute()
at HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
at HttpApplication.ResumeSteps(Exception error)
at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
at HttpRuntime.ProcessRequest(HttpWorkerRequest wr)
at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)Now, if you want to write to the Event log, you need to add a listener to the system.diagnostics/trace/listeners section in the web.config. Note that this can also be made through coding:
<system.diagnostics>
<trace autoflush="false" indentsize="4">
<listeners>
<add name="MyEventListener" type="System.Diagnostics.EventLogTraceListener" initializeData="Johan1"/>
<!-- if you don't want to write to a file, uncomment this, or remove the assert section below
<remove name="Default" />
-->
</listeners>
</trace>
<assert assertuienabled="false" logfilename="c:\AssertLog.txt"/>
</system.diagnostics>The "initializeData" specifies the source of the Event log to write to. Note that the event source must exist first. The following message is written to the Event log:
Fail: Assert happened! A detailed message for you guys!
Pity the stack trace isn't written there too, as it is in the log file. You'd have to write your own TraceListener for that, which is what I did to send the trace message in a mail. To do that, inherit from TraceListener and implement a few lines of code. This is a quick and dirty VB sample that I will rewrite and refactor into c# later:
Imports System.Diagnostics
Imports System.Web.MailPublic Class SmtpTraceListener
Inherits TraceListenerPrivate _recipient As String
Private _sender As String = "some application"
Private _smtpserver As String = "127.0.0.1"Public Sub New()
MyBase.New()
Throw New ApplicationException("SmtpTraceListener must have 'initializeData' attribute specified in config file: recipient,sender,smtpserver")
End SubPublic Sub New(ByVal smtpData As String)
MyBase.New()
Dim arrSmtpData() As String'TODO: Change this into a more decent string, like "sender=asdas;recipient=asdas,smtpserver=asdas"
arrSmtpData = smtpData.Split(","c)
If arrSmtpData.Length > 0 Then
_recipient = arrSmtpData(0)
End IfIf arrSmtpData.Length > 1 Then
_sender = arrSmtpData(1)
End IfIf arrSmtpData.Length > 2 Then
_smtpserver = arrSmtpData(2)
End IfEnd Sub
Private Sub send(ByVal subject As String, ByVal message As String)
SmtpMail.SmtpServer = _smtpserver
SmtpMail.Send(_sender, _recipient, subject, message)
End SubPublic Overloads Overrides Sub Write(ByVal message As String)
send("application message", message)
End SubPublic Overloads Overrides Sub WriteLine(ByVal message As String)
Write(message)
End SubPublic Overloads Overrides Sub Fail(ByVal message As String, ByVal detailMessage As String)
send(message, detailMessage)
End SubEnd Class
Note that a stuck smtp-data in the "initializeData" attribute, that's the way the DiagnosticsConfigurationHandler reads the config section and initalize the listener constructor. A cooler way to do this is to add your own config-section, which I will do later on I think.
Now, just add the listener to the web.config file:
<add name="MySmtpListener" type="AssertTest.SmtpTraceListener, AssertTest"
initializeData="recipient@company.com,sender@company.com,smtp.company.com"/>
I guess there will be better TraceListeners in Whidbey and Longhorn, but I haven't got my hands on those bits yet.
-
Two Towers Extended Edition
Tomorrow they release the extended edition of the Two Towers movie on DVD around here. I've been really looking forward to this one. There have been numerous reviews (over 1400!!) posted on Amazon already, and average customer review is close to 4.5 stars out of 5, which isn't surprising.
-
Watching Your Server Processes
Kent Sharkey has published an article on MSDN, which looks useful for ASP.NET application developers. Summary:
"Learn how to create an ASP.NET HTTP Handler to view the life and death of the processes used by a Web site. In addition, learn how to create a configuration section handler."
I like the end statement:
"Once the HTTP Handler is created, and in place, you should be much more aware of what is happening in this important process. And you'll have time for a coffee, instead of having to hunt down the reason your Web site process restarted."
-
SMTP/POP3 transports with Indigo
-
[interop] Consuming a .NET Typed DataSet from Java (continued)
I got a question about what libraries JDeveloper uses for the soap proxy, and I had a quick look at that. I've just started doing soap stuff in Java, but it seems that the proxy stub that Jdev automatically produce for you uses the org.apache.soap package for most of the soap handling and the oracle.soap package for the http transport.
For my simple code experiment, the request is sent as a org.apache.soap.messaging.Message and the response is received into a org.apache.soap.Envelope. The body goes into a org.apache.soap.Body that contains a vector of body-entries, but the interesting entry is the first one, which is an org.w3c.dom.Element containing the returned XML-data. This code is automatically generated by Jdev, pretty similar to the proxy code generated by VS.NET I guess.
-
Off to aikido practice
I've spent the whole day moving a new asp.net application from test to production. Amazing how things fail just because there is a tiny, tine difference between the test and production site :/
Things work fine now anyway, and I'm off to my aikido practice!! Feels so good to toss people around after a release :D
-
[interop] Consuming a .NET Typed DataSet from Java
Well, as with most things it's dead simple once you get the hang of it. With the help from Jan-Erik, Jdev 10g and XML Spy 4.0 it's not that hard. First I coded up a couple of Web Services in .NET. One method returned a typed DataSet containing both authors and titles, another returned a "typed" XML document (node actually) version created from the typed DataSet:
public XmlNode GetTypedXmlDocument()
{
PubsDataSet dsp = GetTypedDataSets();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.LoadXml(dsp.GetXml());
return xmlDoc.DocumentElement;
}It's very simple to generate SOAP client code from within Jdev that will call my Web Service methods, so I won't get into that. Both my .NET web methods will return an org.w3c.dom.Element (Jdev detects this automatically) and if you're into XML and XPath, you can very well start hacking away at the XML data you got. But being used to .NET typed DataSets, this isn't what I like. So, what you do is get hold of XML Spy and point at the schema location of the typed PubsDataSet in it. When the schema has been loaded into XML Spy, goto DTD/Schema->Generate Program Code... and select Java as the program language. XML Spy will now generate a couple of classes for you based on that Schema, which you now can import and use in Jdev:
Service1Stub ws = new Service1Stub();
try
{
//Testing typed DataSet, need to manipulate the stub code
Element e = ws.GetTypedDataSets();
PubsDataSetType ds = new PubsDataSetType((Element)e.getElementsByTagName("PubsDataSet").item(0));
System.out.println(" --- Typed DataSet --- ");
System.out.println(" No of authors: " + ds.getauthorsCount());
System.out.println(" No of titles: " + ds.gettitlesCount());
System.out.println(" An author: " + ds.getauthorsAt(0).getau_fname());
//Testing "typed" xml document
ds = new PubsDataSetType(ws.GetTypedXmlDocument());
System.out.println(" --- Typed XmlDocument --- ");
System.out.println(" No of authors: " + ds.getauthorsCount());
System.out.println(" No of titles: " + ds.gettitlesCount());
System.out.println(" An author: " + ds.getauthorsAt(1).getau_fname());
}
catch (Exception e)
{
System.err.println("Exception getting DataSet:" + e.toString());
}Sorry about the non-existing colors ;)
As you can see, for the first sample you need to get hold of the first occation of the "PubsDataSet" node because the typed DataSet contains schemas for author and titles, which the XML Spy generated classes doesn't like :) This is not needed for the "typed" xml document, because it only contains the xml data we're interested in. The first example (the typed DataSet) also need a little hack in the Jdev generated service-stub:
public Element GetTypedDataSets() throws Exception
{
URL endpointURL = new URL(_endpoint);...removed a lot of proxy code...
// return (Element)fromElement((Element)responseData.elementAt(0), org.w3c.dom.Element.class);
return (Element) responseData.elementAt(0);
}That's about it. As you can see, the "typed" xml document, need no cryptic code-changes at all. Just feed the return XML Element to the PubsDataSetType constructor, and you're home. The output from above is:
--- Typed DataSet ---
No of authors: 23
No of titles: 18
An author: Johnson
--- Typed XmlDocument ---
No of authors: 23
No of titles: 18
An author: MarjorieI'll ask Jan-Erik and see if we cannot write some kind of article about this. If anyone is interested that is :)
-
Filtering strongly typed DataSets
Is there an easier way to filter on stongly typed DataSets?
public PubsDataSet GetFilteredTypedDataSets(PubsDataSet dsp, string filter)
{
//filter the rows from a copy of the authors table
DataRow[] foundRows = dsp.authors.Copy().Select(filter);//delete the authors from the typed dataset
dsp.authors.Clear();//merge the filtered rows back to the typed dataset
dsp.Merge(foundRows,false,MissingSchemaAction.Add);return dsp;
}If you don't want the DataRow merged back to the typed DataSet, you can work with them directly by casting the DataRow to a typed version:
foreach(PubsDataSet.authorsRow r in foundRows)
Console.WriteLine(r.au_fname);DataSets are really nice, and strongly typed DataSets even more so :)
-
I need more memory!
I'm a total nerv wreck after 30 minutes with Oracle jdev 10g. The editor is cool, but I have too little memory on this machine to play with it. The memory swapping on disk makes me crazy, I can't continue I'm afraid. I'll have to use my laptop (which I'm not allowed to plug in here where I sit at the moment) and use my JOS-player (combined mp3 player and USB Flash Disk) to transfer files between the computers :)
Later today (if time permits), I'll try to consume a .NET DataSet from a .NET web service from java. My friend Jan-Erik has already done it, both with and without the help of XML Spy. I want the know-how too and you better be prepared. Suddenly the boss pops in and asks you to do some java/net interop :)