Sean Carpenter

Thoughts on Development

  Home  |   Contact  |   Syndication    |   Login
  16 Posts | 1 Stories | 1 Comments | 6 Trackbacks

News



Twitter












Archives

Post Categories

About

Friday, November 26, 2010 #

Log4j includes an SMTP appender that can be used to send logging messages via email. As of log4j 1.2.16, this built-in appender has the ability to send messages via Gmail.

Here is an example log4j configuration that uses Gmail to send logged messages:

log4j.rootLogger=DEBUG, gmail
log4j.appender.gmail=org.apache.log4j.net.SMTPAppender
log4j.appender.gmail.SMTPProtocol=smtps
log4j.appender.gmail.SMTPUsername=user@gmail.com
log4j.appender.gmail.SMTPPassword=passw0rd
log4j.appender.gmail.SMTPHost=smtp.gmail.com
log4j.appender.gmail.SMTPPort=465
log4j.appender.gmail.Subject=Logging Message via Gmail
log4j.appender.gmail.To=recipient@somewhere.com
log4j.appender.gmail.From=user@gmail.com
log4j.appender.gmail.layout=org.apache.log4j.PatternLayout
log4j.appender.gmail.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss} [%M] %-5p %C - %m%n
log4j.appender.gmail.BufferSize=5

The SMTPUsername, SMTPPassword, Subject, To, and From properties should be replaced with values appropriate for your situation. One thing that tripped me up originally is that the SMTPPort should be set to 465, not 587 as I had originally done.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Friday, October 31, 2008 #

I was recently working on creating a custom configuration section (deriving from ConfigurationSection) to provide some configuration metadata to an app I’m working on.  In creating a ConfigurationElement, I wanted to validate that one of the attributes of the element was a valid value for the application.  While looking at the example here I noticed the use of various validation attributes applied to validate the format of configuration values.

The CallbackValidatorAttribute and CallbackValidator seemed like they would fit the bill for doing custom validation.  Unfortunately, the documentation on CallbackValidatorAttribute is severely lacking.  Here’s what I found out using Reflector:

  • Both the CallbackMethodName and Type properties of the CallbackValidatorAttribute must be specified.  The Type property is the type of object that contains the CallbackMethodName method.
  • The callback method must be public, static and be a ValidatorCallback delegate.
  • To fail validation, throw an ArgumentException during execution of the callback.  Reflector isn’t actually any help here for the CallbackValidator, but this is what the other validator types do.

I’ve created a small sample demonstrating the use of the CallbackValidatorAttribute.  You can get it here.  The sample validator itself isn’t very useful, but hopefully it illustrates the concept well enough.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Wednesday, July 23, 2008 #

I've been trying to get NCover to run successfully on Vista Ultimate x64 on and off for the last month. We're still using one of the free versions of NCover available from here, so there is no built-in x64 support. I came across this post today that mentioned using corflags to set the 32BIT flag in both NCover and your testing application (the post is describes using NUnit, but this worked for me with MBUnit as well).

Those changes got me past the

Profiled process terminated. Profiler connection not established.

error, but I was then confronted with a new wrinkle:

Index was outside the bounds of the array.

This error would appear at the end of the profiled MBUnit run. I poked around some and found some old posts on the NCover forums talking about this error and how it didn't occur in NCover 1.5.5. So I downloaded 1.5.5 (I had been using 1.5.8) and now all is good - coverage runs successfully!

We're not doing that much with NCover at this point, so I don't think we're giving up anything going with 1.5.5. Maybe someday we'll be able to purchase a current license that supports x64 out of the box.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Friday, December 14, 2007 #

There is a discussion occurring on the ASP.NET forums about passing data to Master Pages using ASP.NET MVC.  I couldn't figure out how to post code in the forums, so this post contains an example of the solution I am currently using.

I defined a "container" class that contains the data for the specific view and the data needed by the master page.  It looks like this:

public class ViewDataContainer<T>
{
public ITopMenuData TopMenu { get; set; }
public ILeftMenuData LeftMenu { get; set; }
public T Data { get; set; }
}

As you can see, this class is generic on the type of view data that it contains.

Next, I created a custom Controller-derived class that looks like this:

public class ControllerBase<T> : Controller
{
protected override void RenderView(string viewName, string masterName, object viewData)
{
ViewDataContainer<T> container = new ViewDataContainer<T>
{
TopMenu =
new TopMenuData(),
LeftMenu =
new LeftMenuData(),
Data = (T)viewData
};
base.RenderView(viewName, masterName, container);
}
}

This class takes the view data that's passed in by the controller and wraps it up in an instance of ViewDataContainer<T> that is then passed along.  As to where to get the TopMenuData and LeftMenuData, I'm still working on that :).

In this incarnation, views need to derive from ViewPage<ViewDataContainer<T>> (specifying T) and then access their view data via the ViewData.Data property.  Today I was playing around with a ViewPage<T> derived class that would expose the typed view data directly.  Maybe I'll post that soon as well.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Saturday, May 27, 2006 #

I've been playing with the National Digitial Forecast Database (NDFD) provided by the National Weather Service.  You can retrieve a forecast via a SOAP web service by providing your latitiude and longitude and a few other details.  The web service worked fine when I called it from Python, but I was getting an error when calling it from .NET (using the generated proxy classes in VS 2003 and VS 2005).  It turns out that the service needs HTTP version 1.0, not 1.1 (the default in .NET).  Unfortunately, the generated proxy class doesn't expose the ProtocolVersion property of the underlying HttpWebRequest object.  So, I added support for this property.

It's a little easier with VS 2005 because of the support for partial classes:

public partial class ndfd {
    private Version _protocolVersion = System.Net.HttpVersion.Version11;
    public Version ProtocolVersion {
        get { return _protocolVersion; }
        set { _protocolVersion = value; }
    }

    protected override System.Net.WebRequest GetWebRequest(Uri uri) {
        System.Net.WebRequest req = base.GetWebRequest(uri);
        ((System.Net.HttpWebRequest)req).ProtocolVersion = _protocolVersion;
        return req;
    }
}

I just added a new file to my solution and added a partial class definition (ndfd is the proxy class generated for the web service) that adds a ProtocolVersion property. Then in the GetWebRequest method, I set the protocol version of the WebRequest.

For VS 2003, I just derived a new class from the generated proxy class and added my property and override there:

public class ndfd2 : ndfd {
    private Version _protocolVersion = System.Net.HttpVersion.Version11;
    public Version ProtocolVersion {
        get { return _protocolVersion; }
        set { _protocolVersion = value; }
    }

    protected override System.Net.WebRequest GetWebRequest(Uri uri) {
        System.Net.WebRequest req = base.GetWebRequest(uri);
        ((System.Net.HttpWebRequest)req).ProtocolVersion = _protocolVersion;
        return req;
    }
}

Then I can just use the new class in my code. Both of these approaches avoid doing anything to the actual generated class, so the Web Reference can be updated without losing any of the customizations.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Wednesday, April 19, 2006 #

Well, after a relatively short CTP, SQL Server 2005 SP1 has been released.  This should be pretty cool, since the CTP solved the problems I was having with Import/Export.

[Found via David Hayden]

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Tuesday, March 28, 2006 #

This is actually pretty basic, but I had a hard time with it this morning, so I thought I'd share how to do this.

I created a host and host instance on our BizTalk server specifically to host the receive locations we are running.  Creating the host and instance was no problem, but I had a hard time changing the receive locations to use the new host.  In the BizTalk Administration Console as well as in BizTalk Explorer, there is a place to choose the Receive Handler - the problem was that this dropdown contained only the original host, not the new one.

It turns out that the host is associated with the receive adapter, not each instance.  So the solution is to go to the Adapters node in the BizTalk Administration Console, choose the Adapter, choose Receive Locations, and then specify the host.  This changes it for all receive locations using that host.  It's actually documented here, so I'm not sure why I had so much trouble finding the answer.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Friday, March 17, 2006 #

The SQL Server 2005 SP1 CTP is available for download.  I'm going to be trying it out immediately since one of the included fixes is for the problem I was having with the Import/Export wizard.
  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Wednesday, February 15, 2006 #

We back up our SQL Server databases to a central storage machine using UNC paths in the BACKUP statement (BACKUP DATABASE db1 TO DISK='\\storage\Backups\db1\db1.bak...). For a while, backups were failing with the message:
BackupDiskFile::RequestDurableMedia:  failure on backup device '\\storage\Backups\db1\db1.bak'. Operating system error 64(The specified network name is no longer available.).

I struggled for a while to find a solution and finally found this thread on www.msce.ms.  Unfortunately that seems to have disappeared, so this is a summary of what that thread recommended (and what worked for me):

  • Open the registry of the SQL Server machine and navigate to HKLM\System\CurrentControlSet\Services\lanmanworkstation\parameters
  • Create a new DWORD value named SessTimeout (if it's already there I guess you can change the value but it wasn't there on any server I was having the problem with).
  • Set the value to decimal 600 (hex 0x258) - I believe this is seconds.

This corrected the problem for me (on two machines I had to bump the value to 900) and seems to have no ill effects on the rest of the server.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Saturday, November 19, 2005 #

We have a web service that is exposed on the internet but is only used by our client application.  The client uses SSL to connect to the server and we are using WS-Security to provide authorization.  Even so, I wanted to prevent someone from viewing the interface of the service by going to the default WSDL generated by ASP.Net (http://domain/Service.asmx?WSDL).

It turns out that this is actually pretty easy to do.  I found documentation on it related to Visual Studio Team System Application Designer, but that page mentions that it makes a change to a service's web.config file.  The answer is to remove the "Documentation" protocol from the <webServices>, <protocols> section of the config file.  I chose to do it by using a <remove> directive in the service's web.config file, but you could also do it in the machine.config file to affect the whole server.  The nice thing about this solution is that I can leave it enabled on our development server so that Visual Studio can auto-generate the client proxy, but it won't be exposed at all on the production site.

Here's my change:

<webServices>
    <protocols>
        <remove name="Documentation" />
    </protocols>
</webServices>
  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Wednesday, October 26, 2005 #

I struggled with this a while back and finally came up with a solution that works.  There are a few things that I would do differently now (I'll try to highlight them below), but I've been running this for a few months now and it produces correct output so I'm happy.

My previous experience with NAnt involved using the <solution> task which works very well.  Unfortunately, the <solution> task doesn't support the Compact Framework.  The solution I came up with is as follows: write a list of source files to compile to a file, convert any resources from .resx format to binary resources, write a list of resource files to include to a file, and finally call the command line compiler passing in these lists of files and any other options.  I'll do a quick description of each step below.

Step 1 - Get a list of source files

This is the NAnt target I use to get the list of files (this is one of those things I'd do differently now - I'd use the /recurse option to the compiler).

<target name="listFiles">
        <delete file="sourceList.txt" if="${file::exists('sourceList.txt')}" />
        <foreach item="File" property="fName">
            <in>
                <items>
                    <include name="*.vb" />
                    <include name="${subDir}/**/*.vb" />
                </items>
            </in>
            <do>
                <echo message="&quot;${fName}&quot;" append="true" file="sourceList.txt"/>    
            </do>
        </foreach>
    </target>

This deletes the file if it already exists, then uses the <foreach> task to loop through the files with a "vb" extension in the specified directory and sub-directory (contained in the subDir property) and <echo> their names to the "sourceList.txt" file.  Pretty straightforward.

Step 2 - Convert Resources

The resources step is something that completely slipped my mind at the beginning.  I was originally only building a class library (with no embedded resources) so skipping this step wasn't an issue.  It became a problem when I added a WinForms app to the solution and built it without including the resources (that didn't run so well since many control properties are stored in the resource files).  Here's the target for building the resources.

<target name="makeResources">
        <foreach item="File" property="fName">
            <in>
                <items>
                    <include name="*.resx" />
                    <include name="${subDir}/**/*.resx" />
                </items>
            </in>
            <do>
                <property name="resName" value="${path::get-file-name-without-extension(fName)}.resources" />
                <exec program="C:\NET\CFDLL\CFResGen" commandline="${fName} ${path::combine(subDir, resName)}" />
            </do>
        </foreach>
    </target>

This target uses the <foreach> task again, this time to go through all .resx files.  This time, instead of writing the file names to a file, the task uses the <exec> task to execute the CFResgen tool, which converts .resx format files to binary .resources files.  I get the filename by stripping the extension off of the .resx file and replacing it with .resources.

Step 3 - Get a list of resource files to include

This is very similar to Step 1, but here's the target for completeness' sake.

<target name="listResourceFiles">
        <delete file="sourceResource.txt" if="${file::exists('sourceResource.txt')}" />
        <foreach item="File" property="fName">
            <in>
                <items>
                    <include name="*.resources" />
                    <include name="*.png" />
                    <include name="${subDir}/**/*.resources" />
                    <include name="${subDir}/**/*.png" />
                </items>
            </in>
            <do>
                <property name="resName" value="${path::get-file-name(fName)}" />
                <echo message="/resource:&quot;${fName}&quot;,&quot;Company.Project.${subDir}.${resName}&quot;" append="true" file="sourceResource.txt"/>    
            </do>
        </foreach>
    </target>

You can see that I'm also getting all files with a .png extension (I had one image embedded as a resource).  The other difference is that when writing the information to the file, I'm writing it as "/resource:fileName, fully qualified resource name". This is the format expected by the compiler and matches the way resources are named when compiling in Visual Studio.

Step 4 - Call the compiler

The last step is to actually call the compiler, passing in the list of source files, the list of resources, plus any other options you may need.  This is another step I'd do differently - I'd use the <vbc> task instead.

<target name="compileProj">
        <mkdir dir="build\Debug\${subDir}" unless="${directory::exists('build\Debug\' + property::get-value('subDir'))}" />
        <property name="cmdLine" value="@${subDir}.rsp @sourceList.txt" />
        <property name="cmdLine" value="${cmdLine + ' @sourceResource.txt'}" if="${file::exists('sourceResource.txt')}" />
        <exec program="C:\WINNT\Microsoft.NET\Framework\v1.1.4322\vbc.exe" commandline="${cmdLine}" output="compileData_${subDir}.txt"/>
    </target>

Here, I first make a directory to put the compiled file in.  Then, I build up the command line including an options file, the source file list, and the resource file list (if it exists).  Lastly, I use the <exec> task again to execute the compiler.  The options file looks like the following:

/warnaserror
/t:winexe
/netcf
/sdkpath:c:\net\cfdll
/out:build\Debug\programName\programName.exe
/r:c:\net\cfdll\system.dll
/r:c:\net\cfdll\system.data.dll
/r:c:\net\cfdll\system.xml.dll
/r:c:\net\cfdll\system.drawing.dll
/r:c:\net\cfdll\system.windows.forms.dll
/r:c:\net\cfdll\system.windows.forms.datagrid.dll
/r:build\Debug\DataAccess\DataAccess.dll
/r:build\Debug\CustomControls\CustomControls.dll
/debug+
/optionstrict+
/imports:Microsoft.VisualBasic
/imports:System
/imports:System.Collections
/imports:System.Configuration
/imports:System.Data
/imports:System.Diagnostics
/imports:System.Drawing
/imports:System.Windows.Forms
/rootnamespace:Company.Project

 

You can see what each of these options do on MSDN.  Basically, they pass in the equivalent settings that Visual Studio uses when it compiles a project.

Wrapping Up

One final target I'll show here is the one that is actually called by NAnt.  It sets the "subDir" property and then calls the other tasks shown above.

<target name="compileMultiple">
        <property name="subDir" value="CustomControls" />
        <call target="listFiles" />
        <call target="getResources" />
        <call target="compileProj"/>
        <property name="subDir" value="DataAccess" />
        <call target="listFiles" />
        <call target="getResources" />
        <call target="compileProj"/>
        <property name="subDir" value="programName" />
        <call target="listFiles" />
        <call target="getResources" />
        <call target="compileProj"/>
    </target>

 

If anyone has any questions (or wants to see the complete files), feel free to use the Contact link on this blog.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Monday, October 24, 2005 #

Now that the release of VS2005 and the .NET Framework 2.0 is imminent, I'm able to justify more time at work for reviewing its impact.  This list of breaking changes for 2.0 is definitely useful in that regard.

I think one of the most important changes is the fact that unhandled exceptions will now always end a process (search for “Unhandled exceptions will always be fatal to a process”on this page).  The 1.1 behavior was that unhandled exceptions on threads other than the main thread would not end a process.  I ran into this twice while working on Windows Services that used background threads.  I know I should have been catching the exceptions, but it was still design/debug time and that error handling wasn't present yet.  The symptom I experienced was that the service would run for a while, but then stop polling a message queue for any more messages.  The problem was that the polling was done in response to the Elapsed event of a non auto-reset timer - when the processing was complete, the timer was re-enabled.  When an exception was raised in the processing (which took place on a background thread as a result of the timer Elapsed event) the processing stopped before getting to the re-enable code.  So essentially the service was dead, since it was no longer polling the message queue.  This change will prevent this kind of bug from creeping in.

[Via James Manning]

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Geoff Appleby posts a link to a Microsoft support page that can tell you what DLL came from what package.  Very cool.

Where'd that DLL come from?

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Thursday, October 20, 2005 #

This is a bookmark for myself as much as anything else, but here is Raymond Lewallen's script for generating a text based dependency report for a SQL Server database.

Sql dependency report from query analyzer

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

One thing I forgot to mention in yesterday's post: it is possible to have SQLXML do the conversion from Base64 to binary using an updategram.  When using an updategram, it is possible to specify a mapping schema, which lets you use attribute names in the updategram that are different than the column names in the table.  A mapping schema also lets you specify data type conversion, one of which is Base64 to binary.  Here's a brief example:

Mapping Schema

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:sqlms="urn:schemas-microsoft-com:mapping-schema">
  <xsd:element name="binaryTest">
   <xsd:complexType>
        <xsd:attribute name="someParameter" sqlms:field="someValue" type="xsd:integer" /> 
        <xsd:attribute name="attachmentData" sqlms:field="attachmentData" type="xsd:base64Binary" sqlms:datatype="image" />
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Updategram

<ROOT xmlns:updg="urn:schemas-microsoft-com:xml-updategram">
   <updg:sync mapping-schema="testMap.xml">
      <updg:before />
      <updg:after>
         <binaryTest someParameter="1" attachmentData="dGhpcyBpcyBhIHRleHQgZmlsZQ==" />
      </updg:after>
   </updg:sync>
</ROOT>

If this updategram is executed as a template query in a virtual directory configured for SQLXML access, a record will be inserted in the “binaryTest” table with the Base64 encoded data converted to binary.

I wasn't able to use this solution for my problem because I couldn't figure out how to get the updategram generated by BizTalk to use a mapping schema.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati