Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

Tuesday, October 21, 2014

A JUnit XSL Transform For PHPUnit Logging

I recently worked on a PHP application with a PHPUnit test suite. Not finding a good way to view the test run results, I settled on logging the output into JUnit format and then applied the style sheet to the XML file so that I can through the XML file into a browser.
time phpunit --testsuite "Application Test Suite" --log-junit public/testresults.xml & sed -i 's//\n/g' public/testresults.xml
Not sure why, but finding a decent transform for the JUnit output was more diffcult than expected. I decided to extended an XSL transform in this StackOverflow answer. It is a pretty good start, plain text, but I can work with it.

After applying Bootstrap styling, the end result is a pleasantly styled JUnit view in a browser. For some reason, FireFox was not applying the bootstrap CSS file, so I just brought in the styles that I needed, thus the style block.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/testsuites">
        <!-- <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html></xsl:text> -->
        <html class="no-js" lang="en">
        <head>
            <meta charset="utf-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <title>PHPUnit Test Results</title>
            <meta name="description" content="" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <!-- <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" /> -->
            <style>
                html {
                  font-family: sans-serif;
                  -webkit-text-size-adjust: 100%;
                      -ms-text-size-adjust: 100%;
                  font-size: 10px;
                  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                }
                body {
                  margin: 0;
                  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
                  font-size: 14px;
                  line-height: 1.42857143;
                  color: #333;
                  background-color: #fff;
                }
                article,
                aside,
                details,
                figcaption,
                figure,
                footer,
                header,
                hgroup,
                main,
                nav,
                section,
                summary {
                  display: block;
                }
                hr {
                  height: 0;
                  -webkit-box-sizing: content-box;
                     -moz-box-sizing: content-box;
                          box-sizing: content-box;
                }
                pre {
                  overflow: auto;
                  font-family: monospace, monospace;
                  font-size: 1em;
                }

                .container {
                  padding-right: 15px;
                  padding-left: 15px;
                  margin-right: auto;
                  margin-left: auto;
                }
                .page-header {
                    padding-bottom: 9px;
                    margin: 40px 0 20px;
                    border-bottom: 1px solid #eee;
                }
                .failure, .error {
                    padding: 0px 20px;
                }
                .failure pre, .error pre {
                    border: 1px solid #DDD;
                    padding: 10px;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <xsl:apply-templates select="testsuite" />
            </div>
        </body>
        </html>
    </xsl:template>

    <xsl:template match="testsuite">
        <header class="page-header">
        <h1>Testsuite: <xsl:value-of select="@name" /></h1>
        <div>
            <xsl:text>
            Tests run: </xsl:text>
            <xsl:value-of select="@tests" />
            <xsl:text>, Failures: </xsl:text>
            <xsl:value-of select="@failures" />
            <xsl:text>, Errors: </xsl:text>
            <xsl:value-of select="@errors" />
            <xsl:text>, Time elapsed: </xsl:text>
            <xsl:value-of select="@time" />
            <xsl:text> sec</xsl:text>
        </div>
        </header>
        <xsl:apply-templates select="system-out" />
        <xsl:apply-templates select="system-err" />
        <div>
            <xsl:apply-templates select="//testcase" />
        </div>
    </xsl:template>

    <xsl:template match="testcase">
        <p>
            <xsl:text>
            Testcase: </xsl:text>
            <xsl:value-of select="@name" />
            <xsl:text> took </xsl:text>
            <xsl:value-of select="@time" />
        </p>
        <xsl:apply-templates select="failure" />
        <xsl:apply-templates select="error" />
    </xsl:template>

    <xsl:template match="failure">
        <div class="failure">
            <span style="color: #ff4136;">
                <xsl:text>
                    Failure:
                </xsl:text>
                <xsl:value-of select="@type" />
            </span>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="error">
        <div class="error">
            <span style="color: #F00;">
                <xsl:text>
                    Error:
                </xsl:text>
                <xsl:value-of select="@type" />
            </span>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="system-out">
        <div>
            <xsl:text>
            ------ Standard output ------
            </xsl:text>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

    <xsl:template match="system-err">
        <div>
            <xsl:text>
            ------ Error output ------
            </xsl:text>
            <pre>
                <xsl:value-of select="." />
            </pre>
        </div>
    </xsl:template>

</xsl:stylesheet>

Tuesday, August 16, 2011

A Domain Specific Language For SharePoint 2010 Deployment

Visual Studio 2010 and SharePoint 2010 has made great improvements to deployments, but still lacks important features. This method is designed to allow for:
  • Easily moving to a new environment
  • Ease of deployment script enhancements without having to change all files
  • XML driven deployment scripts
  • Reduce need for having to know the SharePoint API
To achive this, we will use a Domain Specific Language (DSL) using XML and powershell as the processor.

We'll start out creating the shared environment variables. These are the only part of the method that is environment specific. I generally have one file that I copy to all environments and only uncomment the environment section.
#
# Environment specific variables for use in powershell scripts
#	Usage:
#		. (Join-Path $currentDirectory SharedEnvironment.ps1);
#
#	Maintenance: 
#		Initial development
#

## VM
$SPRootURL = "http://localhost";
$SPEnvironment = "VM";
$SharePointDeploymentFolder = "C:\SharePointDeploy";
$SharePointSolutionCache = "C:\SharePointDeploy\wsp";
## DEV
#$SPRootURL = "http://SPUrlRoot"; # no ending slash
#$SPEnvironment = "DEV";
#$SharePointDeploymentFolder = "C:\SharePointDeploy";
#$SharePointSolutionCache = "C:\SharePointDeploy\wsp";
The processor is the heart of the method. It takes in the XML document and performs tasks based on the documents contents and what actions are implemented.
param (
	[string] $xmlPath = $(Throw 'Missing: xmlPath'),
	[Switch] $remove
)

[void][System.Reflection.Assembly]::LoadWithPartialName( "Microsoft.SharePoint" );
if((Get-PSSnapin | Where-Object {$_.Name -eq "Microsoft.SharePoint.PowerShell"}) -eq $null) { Add-PSSnapIn "Microsoft.SharePoint.Powershell"; }
$currentDirectory = Split-Path $myInvocation.MyCommand.Path; # $myInvocation.MyCommand.Path;
$WSPCache = ".";
. (Join-Path $currentDirectory MemberCatalogEnvironment.ps1);

# Solution Handling
function ProcessSolution( [System.Xml.XmlNode] $solutionNode )
{
	if ($remove) {
		Retract-Solution $solutionNode;
	} else {
		Install-Solution $solutionNode;
	}
}

function Install-Solution( [System.Xml.XmlNode] $solutionNode )
{
	$curDir = Split-Path -Parent $MyInvocation.ScriptName;
	$fileName = $curDir+"\"+$WSPCache+$solutionNode.File;
	$name = $solutionNode.File;
	
	try
	{
		if (!(test-path $fileName)) {
			$(throw "The file $path does not exist.");
		}
	
		$solution = Get-SPSolution $name -ErrorAction SilentlyContinue
		if ($solution -eq $null) {
			Write-Host "Install Solution: $name";
			#Add solution to SharePoint
			
			Write-Host "Adding solution $name..."
			$solution = Add-SPSolution (get-item $fileName).FullName
			
			#Deploy the solution
			if ($solution.ContainsWebApplicationResource -and $solutionNode.AllWebApplications) {
				Write-Host "Deploying solution $name to $webApplication..."
				$solution | Install-SPSolution -GACDeployment -CASPolicies:$false -AllWebApplications -Confirm:$false
			}
			elseif ($solution.ContainsWebApplicationResource) {
				Write-Host "Deploying solution $name to $webApplication..."
				$solution | Install-SPSolution -GACDeployment -CASPolicies:$false -WebApplication $webApplication -Confirm:$false
			} else {
				Write-Host "Deploying solution $name to the Farm..."
				$solution | Install-SPSolution -GACDeployment -CASPolicies:$false -Confirm:$false
			}
		} else {
			Write-Host "Update Solution: $name";
			try {
				Update-SPSolution –Identity $name –LiteralPath $fileName –GACDeployment -CASPolicies:$false -Confirm:$false
			} catch [Exception] {
				$_ | gm;
				if ($_ -contains "Cannot uninstall the LanguagePack 0 because it is not deployed") {
					Retract-Solution $solutionNode;
					Install-Solution $solutionNode;
				}
				Else {
					throw $_
				}
			}
		}

		WaitForJobToFinish $solutionNode.File
	} catch [Exception] {
		Write-Error $_; 
		log -message $_ -type "Error";

	}
}

function Retract-Solution( [System.Xml.XmlNode] $solutionNode )
{
	Write-Host "Retracting solution $solutionNode.Name...";
	
	# Solution must be uninstalled and removed.
	$curDir = Split-Path -Parent $MyInvocation.ScriptName
	$fileName = $curDir+"\"+$WSPCache+$solutionNode.File
	
	try
	{
		[Microsoft.SharePoint.Administration.SPSolution] $solution = (Get-SPSolution $name -ErrorAction SilentlyContinue)[0];
		if (($solution -ne $null) -and ($solution.Deployed)) {
			Write-Host "Retracting solution."

			if ($solution.ContainsWebApplicationResource -and $solutionNode.AllWebApplications) {
				Write-Host "Retracting solution $name to $webApplication..."
				$solution | Uninstall-SPSolution -AllWebApplications -Confirm:$false
			}
			elseif ($solution.ContainsWebApplicationResource) {
				Write-Host "Retracting solution $name to $webApplication..."
				$solution | Uninstall-SPSolution -WebApplication $webApplication -Confirm:$false
			} else {
				Write-Host "Retracting solution $name to the Farm..."
				$solution | Uninstall-SPSolution -Confirm:$false
			}
			#Uninstall-SPSolution -Identity $solutionNode.File -Confirm:$false
			WaitForJobToFinish $solutionNode.File

			Write-Host "Deleting solution."
			Remove-SPSolution -Identity $solutionNode.File -Confirm:$false
		}elseif (($solution -ne $null) -and ($solution.Deployed))
		{
			Write-Host "Deleting solution."
			Remove-SPSolution -Identity $solutionNode.File -Confirm:$false
		}
	} catch [Exception] {
		Write-Error $_;
		log -message $_ -type "Error";
	}
}

# Feature Handling
function ProcessFeatureActivation( [System.Xml.XmlNode] $featureNode, $retry = 2 )
{
	try
	{
		if (-not $remove) {
			[Microsoft.SharePoint.Administration.SPFeatureDefinition] $feature = Get-SPFeature | ? {$_.DisplayName -eq $featureNode.Name};

			if ($feature -eq $null) {
				Install-SPFeature -path $featureNode.Name;
			}

			if( ($featureNode.Url -ne $null) -and ($featureNode.Url -ne "") )
			{
				$url = $SPRootURL + $featureNode.Url;
				Write-Host 'Enable feature:' $featureNode.Name;
				Enable-SPFeature -identity $featureNode.Name -URL $url;
			}
			else
			{
				Write-Host 'Enable feature:' $featureNode.Name
				Enable-SPFeature -identity $featureNode.Name
			}
		}
	} catch [Exception] {
		Write-Error $_; 
		log -message $_ -type "Error";
	}
}

function ProcessFeatureDeactivation( [System.Xml.XmlNode] $featureNode )
{
	try
	{
		if( ($featureNode.Url -ne $null) -and ($featureNode.Url -ne "") )
		{
			$url = $SPRootURL + $featureNode.Url;
			#stsadm -o deactivatefeature -id $featureNode.Id -url $url
			Write-Host 'Disable feature:' $featureNode.Name;
			Disable-SPFeature -identity $featureNode.Name -confirm:$false -url $url;
		}
		else
		{
			#stsadm -o deactivatefeature -id $featureNode.Id
			Write-Host 'Disable feature:' $featureNode.Name;
			Disable-SPFeature -identity $featureNode.Name -confirm:$false;
		}
	} catch [Exception] {
		Write-Error $_; 
		log -message $_ -type "Error";
	}
}

function ProcessScript( [System.Xml.XmlNode] $scriptNode )
{
	"Executing $($scriptNode.Name)...";
	Invoke-Expression $scriptNode."#text";
}

function ProcessCopyFile( [System.Xml.XmlNode] $copyfileNode )
{
	"Copying $copyfileNode.file to $copyfileNode.destination...";
	$curDir = Split-Path -Parent $MyInvocation.ScriptName
	$fileName = $curDir+"\"+$WSPCache+$copyfileNode.file
	xcopy /Y /E /R $filename $copyfileNode.destination
	if ($LASTEXITCODE -ne 0) {
		Write-Error("Error Copying: " + $filename);
	}
}

function Main 
{
	[string]$xmlName = Split-Path -Path $xmlPath -Leaf
	Start-Transcript "$currentDirectory\$xmlName-log.txt";
	Write-Host "Current Directory: $currentDirectory";
	Write-Host "Config: $xmlPath";
	
	if (test-path $xmlPath) {
		# Found the file in the pwd or via the absolution path
		#"Path Found.";
		$configFileItem = Get-Item $xmlPath;
	} else {
		# Attempt to find the Config XML in the script's directory
		#"Path Not Found.";
		$idx = $inputConfigFile.LastIndexOf("\");
		$configFileItem = Get-Item $(Join-Path $currentDirectory $xmlPath.substring($idx+1,$xmlPath.Length-1-$idx));
	}
	#$configFileItem;
	$configXml = New-Object System.Xml.XmlDocument;
	$configXml.Load( $configFileItem.FullName );
	if ($configXml.SharePointDeploymentConfig.WSPCache) {
		$WSPCache = $configXml.SharePointDeploymentConfig.WSPCache;
	}
	Write-Host "WSP cache location: $WSPCache"; Write-Host("");
	
	#foreach ($taskNode in $configXml.SharePointDeploymentConfig.get_ChildNodes()|?{$_ -ne $null}) { ProcessTask $taskNode;  } 
	$configXml.SharePointDeploymentConfig.get_ChildNodes()|?{$_ -ne $null} | % { ProcessTask $_;  }
	
	Write-Host("");
	
	#[Microsoft.SharePoint.Administration.SPFarm]::Local.solutions | format-table -property name, Deployed, DeployedWebApplications, DeploymentState;
	#"---";[Microsoft.SharePoint.Administration.SPFarm]::Local.solutions | ? { $_.LastOperationResult -ne "DeploymentSucceeded" } | % { $_.Name; $_.LastOperationDetails; $_; };"Done.";
	Stop-Transcript;
}

function ProcessTask( [System.Xml.XmlNode] $taskNode )
{
	#Write-Host("");Write-Host("---")
	# For multiple web.config modifications, sleeping between activations reduces errors.
	if ($taskNode.Sleep -ne $null) {
		Sleep-For $taskNode.Sleep;
	}

	if( $taskNode.get_Name() -eq "Solution" )
	{
		#"Solution"
		#$taskNode;
		ProcessSolution $taskNode
	}
	elseif( $taskNode.get_Name() -eq "SiteCollection" )
	{
		#"SiteCollection"
		ProcessSiteCollection $taskNode
	}
	elseif( $taskNode.get_Name() -eq "FeatureActivate" )
	{
		#"FeatureActive"
		ProcessFeatureActivation $taskNode
	}
	elseif( $taskNode.get_Name() -eq "FeatureDeactivate" )
	{
		#"FeatureDeactive"
		ProcessFeatureDeactivation $taskNode
	}
	elseif( $taskNode.get_Name() -eq "BdcAppDef" )
	{
		"BdcAppDef"
		"Not Implemented"
		ProcessBusinessDataCatalogAppDef $taskNode
	}
	elseif( $taskNode.get_Name() -eq "CopyFile" )
	{
		#"CopyFile"
		ProcessCopyFile $taskNode
	}
	elseif( $taskNode.get_Name() -eq "Script" )
	{
		#"Script"
		ProcessScript $taskNode
	}
}

function WaitForJobToFinish([string]$SolutionFileName)
{ 
    $JobName = "*solution-deployment*$SolutionFileName*"
    $job = Get-SPTimerJob | ?{ $_.Name -like $JobName }
    if ($job -eq $null) 
    {
        Write-Host 'Timer job not found'
    }
    else
    {
        $JobFullName = $job.Name
        Write-Host -NoNewLine "Awaiting job $JobFullName"
        
        while ((Get-SPTimerJob $JobFullName) -ne $null) 
        {
            Write-Host -NoNewLine .
            Start-Sleep -Seconds 2
        }
        Write-Host  "Finished."
    }
}

function Pause ($Message="Press any key to continue...")
{
	Write-Host -NoNewLine $Message
	$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
	Write-Host ""
}

function log {
	param (
		[string] $message = $(throw "Please specify a message."),
		[string] $type = "Information",
		[string] $logName = "Application",
		[string] $sourceName = "Logos Inc Deployment"
	)
	
	$EventLog = Get-EventLog -list | Where-Object {$_.Log -eq $logName}
	$EventLog.MachineName = "."
	$EventLog.Source = $sourceName;
	$EventLog.WriteEntry($message, $type, 1103);
}

. main
After all of that, we can create our simple XML files. The number of XML files depends on the number of deployment options required. There could be 1 XML which deploys the whole environment (see example below) and a couple other XML files to deploy a small subset of functionality.
Some options not used in this example and I will leave it to the reader as an exercise to implement:
  • BCS
  • Site collection restore
  • Copy files elsewhere on the servers - this may be needed if placing files in places where the WSP won't deploy to
Note: Due to an apparent limitation in the syntax highlighter functionality, the XML brush does not handle self closing nodes correctly. For presentation purposes, I have changed the self-closing nodes to have end tags.

	
	
		
	
	
	<Script Name="Remove Session">
		Disable-SPSessionStateService;
	</Script>
	
	<Script Name="Remove Blocked Extension">
		& .\RemoveBlockedExtension -webApplication "http://localhost";
	</Script>
	
	
	
	
	

	<Script Name="Enable Session">
		Enable-SPSessionStateService -DatabaseName "LogosIncSessionState" -SessionTimeout 120
	</Script>
	
	
	
	
	



Now that we have the files needed, we can put them into the directory structure:
C:\SharePointDeploy - Powershell Scripts and XML files
C:\SharePointDeploy\wsp - Cache of WSP files and other deployment files
Usage from the C:\SharePointDeploy directory:
.\SPDeploy.ps1 .\Deploy-Logos-EVERYTHING.xml
.\SPDeploy.ps1 .\Deploy-Logos-EVERYTHING.xml -remove

Thursday, August 27, 2009

XQuery Rename/Replace Xml Node and Update K2 Workflow Process Instance Xml Fields

I recently ran across a case where a property in our application was re-termed due to a change in requirements. Updating the code was very easy, however it meant that all of the Xml documents needed to be updated and that was not going to be as easy. It would involve updating the serialized object xml documents in our workflow data repository and K2.

Out of curiosity I ran the process using the old Xml to see what would happen. Deserializing the old Xml documents resulted in everything after that renamed property being omitted from the deserialization and it didn't throw and error. That was unexpected, but good information to know.

As it turns out, updating the workflow data repository was easy. K2 has many issues which resulted in the K2 update sql. The code below shows how I accomplished this:
CREATE PROCEDURE [dbo].[usp_brock_RenameXmlNode]
AS
BEGIN
 -- UPDATE Workflow Instances
 UPDATE [WorkflowInstances] SET InstanceData.modify('
  insert element NewTag {/ObjectXML/OldTag/text()} after (/ObjectXML/OldTag)[1]
 ')
 WHERE  WorkflowID='7B4DEB62-D462-4FE8-A8E2-2057B5F31B19'
   AND InstanceData.exist('/ObjectXML/OldTag') = 1
 UPDATE [WorkflowInstances] SET InstanceData.modify('
  delete (//OldTag)[1]
 ')
 WHERE  WorkflowID='7B4DEB62-D462-4FE8-A8E2-2057B5F31B19'
   AND InstanceData.exist('/ObjectXML/OldTag') = 1
 
 -- UPDATE K2 Workflows
 -- Create Table Var and Populate
 DECLARE @K2ProcXml2XmlType table
 (
  ProcInstID INT PRIMARY KEY,
  WorkflowName VARCHAR(128),
  WorkflowID UNIQUEIDENTIFIER,
  InstanceData XML,
  IsModified BIT
 )
 PRINT 'POPULATE TABLE VAR';
 INSERT INTO @K2ProcXml2XmlType 
  SELECT pin.ID
     ,ps.[Name] WorkflowName
     , wi.WorkflowID
     ,CONVERT(XML,REPLACE(CAST(f.string as nvarchar(max)),'<?xml version="1.0" encoding="utf-8"?>','')) InstanceData
     , 0 --IsModified
    FROM [K2].[dbo]._FieldOnDemand f INNER JOIN [K2].[dbo]._ProcInst pin ON f.ProcInstID=pin.ID
    INNER JOIN [DemoDatabase].dbo.WorkflowInstances wi ON wi.ProcessID=pin.ID
    INNER JOIN [K2].[dbo]._Proc p ON pin.ProcID = p.ID
    INNER JOIN [K2].[dbo]._ProcSet ps ON p.ProcSetID = ps.ID
  WHERE  (ps.[Name] in ('WorkflowX'))

 UPDATE @K2ProcXml2XmlType
 SET InstanceData.modify('insert element NewTag {/ObjectXML/OldTag/text()} after (/ObjectXML/OldTag)[1]'), IsModified = 1
 WHERE (InstanceData IS NOT NULL) 
  AND (InstanceData.exist('/ObjectXML/OldTag') = 1)

 UPDATE @K2ProcXml2XmlType
 SET InstanceData.modify('delete (//OldTag)[1]'), IsModified = 1
 WHERE (InstanceData IS NOT NULL) 
  AND (InstanceData.exist('/ObjectXML/OldTag') = 1)


 -- End Result of all updates
 --PRINT 'QUERY RESULT:';
 --SELECT
 --        [ProcInstID]
 --   , WorkflowName
 --   , InstanceData
 --  FROM @K2ProcXml2XmlType src
 --WHERE src.IsModified = 1;

 -- Populate Original Table and Query
 PRINT 'POPULATE TABLE FROM VAR:';
 UPDATE dst
 SET dst.String = CONVERT(ntext,'<?xml version="1.0" encoding="utf-8"?>'+CAST(src.InstanceData as nvarchar(max)))
 FROM @K2ProcXml2XmlType src INNER JOIN [K2].[dbo].[_FieldOnDemand] dst ON src.ProcInstID = dst.ProcInstID
 WHERE src.IsModified = 1;

        -- END K2 Update
END;
GO

exec dbo.usp_brock_RenameXmlNode

DROP PROCEDURE dbo.usp_brock_RenameXmlNode
It runs quite fast.

There are a couple caveats, you can't create the XPath location dynamically (unless you are using dynamic sql which I try to avoid at all costs). This means that you can't pass old and new name parameters into the stored procedure and update the records. Disappointing as it is, any future renames should require an easy find and replace.

I tried using a cursor to do this type of updating, but the resulting SQL in execution times in more than more than 90 minutes (I cancelled it at that point) and verified with the DBA's that I was not doing something wrong, I abandoned that path and went back to the set based updates.

Sunday, February 22, 2009

Finding the Root Node Name with XQuery and SQL Server 2005

I recently had a case where i needed to test for the existence of a node in a query involving an XML. After researching I found a lot of posts indicating this as the solution:
DECLARE @x xml
SET @x = '
123 Main St.
' DECLARE @query varchar(30) SET @query = '/address/street' SELECT @x.exist('sql:variable("@query")')
A key point to make is that the ".exist" is case sensitive; using ".Exist" causes an error. The result is "1", however when i would change @query to an invalid xpath, I would still get "1". Never the less, it was still a good start toward figuring out how to find the root node tag name.

Many posts I found used the 'sql:variable("@variable")' as a parameter in a xpath query. I am quite experienced w/ XPath, especially with Xsl, so I applied it to my situation and came up with this (simplified for example)...
DECLARE @tag varchar(30)
SET @tag = 'address'

SELECT ID, XmlField
FROM sometable
WHERE XmlField.exist('/*[1][local-name() = sql:variable("@tag")]')
The result of this one was 1. If a changed @tag to an invalid input, the result was 0. This is exactly what I was expecting and thus my solution.

Taking this further, the tag name can be returned by extending the previous example.
DECLARE @tag varchar(30)
SET @tag = 'address'

SELECT ID, XmlField.query('local-name(/*[1])'), XmlField
FROM sometable
WHERE XmlField.exist('/*[1][local-name() = sql:variable("@tag")]')
There you go, hope it helps.