Thursday, July 30, 2009

Custom NAnt ILMerge Task. No Assembly Required

ILMerge is a Microsoft utility that enables you to incorporate referenced .NET assemblies into a combined assembly, removing the need to distribute external assemblies. If you are using ILMerge, and you have an automated build process using NAnt, you have several options for integrating ILMerge into your build:

1. You can you use the <exec> task and build your command line arguments as a string.

2. You can use a 3rd party (or write your own) custom tasks library like this one: http://code.google.com/p/ilmerge-tasks/wiki/HowToUse

3. Or you can write a inline custom task using the <script> tag in your NAnt script


I started with option #1. I quickly realized that without <fileSet> support, this could become a difficult option to maintain.

I then moved to option #2. This library didn’t work for me. I suspect it’s because my \tools folder, which contains ILMerge.exe and the ILMergeTask.dll are both on an R:\. And I suspect this is troublesome for the .net reference search. I get back an error that ILMerge was not found.

Which left me with option #3. NAnt presents a <script> tag which allows you to write extensions to core functionality. So I wrote my own ILMergeTask. The thing to note here is that I could have compiled this into a task library as well. The real difference verse option #2 is the ILMergeTask I wrote extends ExternalProgram (as opposed to Task). Which allows me to pass in the path of ILMerge.exe.

Usage is pretty familiar:

<ilmerge outputfile="Combined.Assembly.exe"
program=”R:\tools\ilmerge\ilmerge.exe”
primary=”Primary.Assembly.exe”
log=”${log.dir}\ilmerge.log”>
<assemblies>
<include name="*.dll" />
</assemblies>
</ilmerge>






And here is the code for the custom task:
  <script language="C#" prefix="custom" >
<references>
<include name="System.dll" />
<include name="NAnt.Core.dll" />
</references>
<imports>
<import namespace="System" />
<import namespace="System.Collections" />
<import namespace="System.Collections.Specialized" />
<import namespace="NAnt.Core.Types" />
<import namespace="NAnt.Core.Util" />
<import namespace="NAnt.Core.Tasks" />
</imports>

<code>
<![CDATA[
[TaskName("ilmerge")]
public class ILMergeTask : ExternalProgramBase {

private FileSet m_assemblies;
private string m_logFile;
private string m_outputFile;
private string m_primaryFile;


[TaskAttribute("program", Required = true)]
[StringValidator(AllowEmpty = false)]
public override string ExeName
{
get { return base.ExeName; }
set { base.ExeName = value; }
}


public override string ProgramArguments
{
get { return string.Empty; }
}

[BuildElement("assemblies", Required=true)]
public virtual FileSet InputAssemblies
{
get
{
return this.m_assemblies;
}
set
{
this.m_assemblies = value;
}
}

[TaskAttribute("logfile")]
public virtual string LogFile
{
get
{
if (this.m_logFile == null)
{
return null;
}
return this.Project.GetFullPath(this.m_logFile);
}
set
{
this.m_logFile = StringUtils.ConvertEmptyToNull(value);
}
}

[TaskAttribute("primary", Required=true), StringValidator(AllowEmpty=false)]
public virtual string PrimaryFile
{
get
{
if (this.m_primaryFile == null)
{
return null;
}
return this.Project.GetFullPath(this.m_primaryFile);
}
set
{
this.m_primaryFile = StringUtils.ConvertEmptyToNull(value);
}
}

[TaskAttribute("outputfile", Required=true), StringValidator(AllowEmpty=false)]
public virtual string OutputFile
{
get
{
if (this.m_outputFile == null)
{
return null;
}
return this.Project.GetFullPath(this.m_outputFile);
}
set
{
this.m_outputFile = StringUtils.ConvertEmptyToNull(value);
}
}


protected override void ExecuteTask()
{
try
{
Log(Level.Info, "Executing ILMerge.exe");
Log(Level.Info, string.Format("/out:\"{0}\"", m_outputFile));
Log(Level.Info, string.Format("/log:\"{0}\"", m_logFile));
Arguments.Add(new Argument(string.Format("/out:\"{0}\"", m_outputFile)));

Log(Level.Info, string.Format("assembly[{0}]: {1}", "primary", m_primaryFile));
Arguments.Add(new Argument(string.Format("\"{0}\"", m_primaryFile)));

for (int i = 0; i < m_assemblies.FileNames.Count; i++)
{
Log(Level.Info, string.Format("assembly[{0}]: {1}", i, m_assemblies.FileNames[i]));
Arguments.Add(new Argument(string.Format("\"{0}\"", m_assemblies.FileNames[i])));
}

Arguments.Add(new Argument(string.Format("/log:\"{0}\"", m_logFile)));

base.FailOnError = false;
base.ExecuteTask();
}
catch (Exception ex)
{
throw new BuildException(string.Format("Error executing ILMerge {0}", "test"), Location, ex);
}
}
}
]]>
</code>
</script>




Big kudos to the guy on google code and the guy that wrote reflector ;-)

Friday, July 17, 2009

How to harden your iMac Apache Web Server

Note: I am running Leopard 10.5.6 and Apache 2.2. Other versions of OS X and/or Apache may work differently.

A few months ago I started Apache Web Server on my iMac and today I’m going to apply the “minimum necessary” configuration to Apache, so it is more secure. In a future posts, I plan to write about putting your Apache Web Server "in Jail," and enabling DDNS ... so that your hardened iMac will be a web host on the Internet.

Prerequisite
When I first bought my iMac, I secured it using Apple’s Security Configuration as a guide. If you buy your iMac at Best Buy, you can also pay Geek Squad $40 to do this for you. If you’re looking for an abridged version, this guide is also good - http://www.macshadows.com/kb/index.php?title=Hardening_Mac_OS_X. Also, Apple puts out an audit tool with lots of other security tips - http://support.apple.com/downloads/Common_Criteria_Tools_for_10_4.

The first thing to do before securing your Apache Web Server is to decide on the functionality you want from Apache. I decided on some very basic items:

  1. Only static HTML pages will be served.
  2. Ok, I’m adding SSI (http://httpd.apache.org/docs/2.2/howto/ssi.html)
  3. The server must support the virtual hosting mechanism.
  4. The server must log all web requests (including information about web browsers).
Configure Apache
To apply these configurations, you have to edit the web server configuration file

Open a terminal: Finder->Applications->Utilities->Terminal
Edit the configuration file: sudo bbedit /private/etc/apache2/httpd.conf

bbedit is a text editor that I bought. You can use any text editor here, even TextEdit which came pre-installed on my iMac.

You do not need to enable root password for this. If you are logged in as an Administrator User you can sudo and execute this command. My finished configuration is as follows (in the interest of space, I removed comments normally found in

ServerRoot "..."
Listen 80
LoadModule authz_host_module libexec/apache2/mod_authz_host.so
LoadModule include_module libexec/apache2/mod_include.so
LoadModule log_config_module libexec/apache2/mod_log_config.so
LoadModule expires_module libexec/apache2/mod_expires.so
LoadModule mime_module libexec/apache2/mod_mime.so
LoadModule dir_module libexec/apache2/mod_dir.so



User www
Group www



ServerAdmin esuyer at gmail dot com
UseCanonicalName Off
ServerSignature Off
HostnameLookups Off
ServerTokens Prod
DocumentRoot ".../www"
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 15

MinSpareServers 5
MaxSpareServers 10
StartServers 5
MaxClients 150
MaxRequestsPerChild 0


Options None
AllowOverride None
Order deny,allow
Deny from all


Options +Includes
Order allow,deny
Allow from all


DirectoryIndex index.htm


Order allow,deny
Deny from all
Satisfy All


Order allow,deny
Deny from all
Satisfy All


Order allow,deny
Deny from all
Satisfy All

ErrorLog "/private/var/log/apache2/error_log"
LogLevel warn

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common


LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio


CustomLog "/private/var/log/apache2/access_log" common



ScriptAliasMatch ^/cgi-bin/((?!(?i:webobjects)).*$) "/Library/WebServer/CGI-Executables/$1"


DefaultType text/plain


TypesConfig /private/etc/apache2/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
AddType text/html .shtml
AddOutputFilter INCLUDES .shtml


Include /private/etc/apache2/extra/httpd-vhosts.conf

SSLRandomSeed startup builtin
SSLRandomSeed connect builtin




Compared to the default configuration file, the following important changes have been made:

  • The number of enabled modules has been reduced to minimum.
  • Apache's processes (except for the root process) are set to be executed with unique regular user/group privileges.
  • Apache discloses the least information about itself as possible.
  • Access rights to the website's content are set to be more restrictive.

Wednesday, July 15, 2009

QNXT Integration Specialist Job Description

If you are planning to customize QNXT with functionality that is specific to your business, you have several options. All of these options revolve around application integration; under the TriZetto license, you do not have the rights to customize the QNXT application itself. You will need to fill several roles in your organization to make this happen. One of these roles is an Integration Specialist. Here is a job description for this role.


Position Description
The Integration Specialist is responsible for designing, developing and implementing software solutions based on requirements from the business, with an emphasis on enterprise solution development utilizing Microsoft BizTalk Server 2006, SQL Server 2005 and .NET. We need a cooperative team player who can work closely with a manager and team members as well as alone. Ability to function well in fast-paced work environment and the ability to work on multiple time-sensitive projects concurrently are necessary. Excellent written and verbal communication skills, as well as detailed-oriented organizational skills are also required. Software development experience should include extensive object-oriented design experience.


Required Skills:
  • Minimum of 7 years in-depth hands-on development experience with Object Oriented Analysis, Design and Development of custom .net applications and application integrations.
  • Minimum of 5 years development experience with C# .NET
  • Minimum of 4 years development experience with SQL Server, and T-SQL
  • One of the following:
  1. Minimum of 2 years development experience with BizTalk 2006
  2. Minimum of 2 years development experience with SSIS 2005
  • Strong verbal/written communication skills
  • Demonstrated ability to be productive independently and/or as a team member
  • Strong organizational, interpersonal and analytical skills


You’ll notice I did not include QNXT experience … QNXT experience would be ideal. QNXT Hosting experience would be nirvana. I had no luck finding people with any QNXT experience in Worcester, MA. And I’m reasonably certain we were well above the average salary range for this position. At the time of writing this, there are exactly 3 QNXT Hosting customers. I’m not sure that QNXT has the market penetration to warrant an effective match on this skill set. In place of QNXT experience, I recommend all Integration Specialists go through this first 90 day plan


First 90 days
Look for more posts on each item below (coming soon on this blog). If you are at the beginning of your implementation project, you may find yourself with some down time while planning is happening. Use this window to allow this 90 day plan to occur. This research/education is invaluable in my opinion.

  • Certified Product Professional – Technical System Integration training. This is a QNXT certification
  • Install QNXT and the QNXT SDK
  • Write a simple .net console app that uses the QNXT SDK i.e. create an authorization
  • If you are a hosting customer,
  1. Write an SSIS package that is HBA Compliant and that adheres to the Self Service deployment structure
  2. Deploy and Execute to hosting using the Self Service facilities
  • Create build/make scripts for your deployment artifacts i.e. using nant or other .net scripting tools
  • Create unit tests for your deployment artifacts i.e. using nunit, fit or other automated testing tools

Tuesday, July 14, 2009

QNXT Custom Database

Warning: this post is not for the faint of heart. I will be discussing the customization of QNXT.


There are two types of customization that you can do with QNXT: configuration and custom coding. Configuration is a business process. You configure QNXT, by defining your business rules using the configuration functionality built in to QNXT. Some common configurations health plans apply in QNXT are the business rules around adjudicating claims, member eligibility verification, and authorizations. There are many business processes that you can configure using the native, or out of the box, functionality provided by QNXT.

Custom coding is a development process. There are two types of custom code development: supported and unsupported. I’m going to cover the differences between supported and unsupported code in another post. Supported and unsupported code are both deployed to the QNXT Custom Database.

Custom coding is not used to enhance the QNXT application itself. Put another way, the QNXT application is not something that can be modified under the QNXT license and support agreement. Custom coding is used to extend QNXT, or to allow for integration with QNXT. These customizations are applied outside of QNXT itself. Some common examples of custom code are extracts and interfaces to other line of business applications like Finance/Accounting, CRM and Case Management.

QNXT allows for several points of integration. The ones we use are: the QNXT SDK (which I will cover in a separate post), and the QNXT Custom Database. The purpose of the QNXT Custom Database is to provide a layer of abstraction over the out of the box QNXT database called Plan Data. Changes that ordinarily could be applied to Plan Data, by way of new stored procedures, views, and other database objects, are applied to this Custom database instead. This way the Plan Data database remains unchanged. And all customer customizations are one place, the Custom database.

The QNXT Custom Database hosts all custom database code, as well as any data that supports these customizations. There is also a QNXT Stage Database. This database is also used for customization. The difference is the Stage database is permanent in structure, but temporary in storage. It is a temp database. If you have an extract for example, that requires some pre-processing or post-processing, you can temporarily host your interim-state data in Stage.

TriZetto Hosting

What I’ve described so far is true of all QNXT installations. These next few paragraphs speak to the specifics of the QNXT Custom Database if you are a hosting customer. Each QNXT environment hosts a QNXT Custom and Stage database. If you have regions that have several environments this likely means that a single SQL Server instance at TriZetto hosts several Custom and Stage databases. So for example, in a single database instance, you may have development, unit test, training and configuration version of the Custom and Stage database. So eight databases in total. This creates an interesting custom code promotion problem. I think a typical scenario that most of us are used to when promoting a change from development, to unit test, to model office and production, you expect the database names in each of these environments to be the same. With the QNXT Custom Database at TriZetto Hosting this is not the case. You promote from Custom_Dev, to Custom_UT, to Custom_PPMO, and Custom_Prod.

There are a couple of solutions to this problem. Trizetto supports a ticket-based promotion process that will support these name changes as you promote up. So in this example, a customer may choose to setup a promotion process where they deliver custom code to the Custom_Dev database, and Trizetto manages the promotion of these changes going forward. This promotion process is custom tailored to customer needs. In our case, we chose our first promotion group to be PPMO. This means we manage the delivery of code to Custom_Dev, Custom_UT and Custom_PPMO, which puts more of this burden on us. We chose to do this because we realized a time savings during our implementation effort by opening fewer tickets. Instead of putting in a ticket (which may have a 24 to 48 hour turnaround depending on the change) to promote to Custom_Dev, we delivered the code there ourselves, an didn’t engage the ticketing system until we needed to deploy to Custom_PPMO. By this time, the custom code had gone through development, quality assurance and user acceptance testing.

If you are a hosting customer, you’ll likely be executing code in the QNXT Custom Database using a job scheduler and the TriZetto Hosted Batch Architecture (also known as the HBA, more on this in another post) which has Common System Properties that allow you to connect to the QNXT Custom Database.

Full Disclosure re QNXT

I hope to be writing a lot about QNXT and TriZetto Hosting on this blog, so I wanted to make this clear to everyone reading this: I do not work for Trizetto, nor am I a partner of TriZetto’s. I work for a health plan that is a TriZetto customer. Everything I am writing here is about my experience and my point of view. My company went through a three year implementation effort with QNXT and TriZetto Hosting, ending in 2010. We were TriZetto’s very first hosting customer. A large part of the reason I started this blog is I am interested in collaborating with the QNXT community. I encourage you to ask a question, leave a comment, tell me about your experience, and/or challenge my views.

Thursday, July 2, 2009

How to connect your Apple TimeCapsule to an existing network

1. Unpack your Apple TimeCapsule

2. Plug in a network cable into a LAN port on your TimeCapsule, and a LAN port on your existing network/router. Then plug in the TimeCapsule power cable to a power outlet. When the TimeCapsule comes on and configures itself, a blinking amber light will be on. This signifies a network connection problem. The next few steps will fix this problem.

3. On your MAC, Airport Utility should pop up after a few minutes. This comes out of the box with Leopard, but it also comes on the install CD that accompanies the TimeCapsule


4. Click the Manual Setup button


5. Click Internet in top navigation. Then under Connection Sharing, select "Off (Bridge Mode)". Then click the Update button. The TimeCapsule will reboot, and will come back up with a solid green status light, signifying it is ready to use.


If you don't need it, recommend you also turn the Wireless Network off.