VB Magic

2013/10/23

Accessing Jenkins API from VB.net

Filed under: .NET, ASP.NET MVC, VB.NET — Tags: , , , , , , — vbmagic @ 3:17 pm

As this was one of those things it took a while to track down I though a quick post would help others trying the same.

I have an up-to-date Jenkins server running builds and I would like to pull a list of failed jobs from the server and display them on a website.
First you need to look up the correct URL to access the list of jobs so after seeing a few posts in blogs and going to the Jenkins Website I worked out it follows this principle.

http{s}//{your Jenkins URL}/api/{xml/json}

If you leave off the xml/json parameter you will get a help page.

I my case I wanted the result in XML as VB.net has good xml integration. So my URL is:

http://mydomain.com:8080/api/xml

This will return something similar to the following XML code:

<hudson>
  <assignedLabel/>
  <mode>NORMAL</mode>
    <nodeDescription>the master Jenkins node</nodeDescription>
  <nodeName/>
  <numExecutors>2</numExecutors>
  <job>
    <name>ProjectBuild</name>
    <url>http://myhost.com:8080/job/ProjectBuild/</url>
    <color>blue</color>
  </job>
  <job>
    <name>IntegrationTests</name>
    <url>http://myhost.com:8080/job/IntegrationTests/</url>
    <color>red</color>
  </job>
  <overallLoad/>
  <primaryView>
    <name>All</name>
    <url>http://myhost.com:8080/</url>
  </primaryView>
  <quietingDown>false</quietingDown>
  <slaveAgentPort>0</slaveAgentPort>
  <unlabeledLoad/>
  <useCrumbs>false</useCrumbs>
  <useSecurity>true</useSecurity>
  <view>
    <name>All</name>
    <url>http://myhost.com:8080/</url>
  </view>
  <view>
    <name>Status</name>
    <url>http://myhost.com:8080/view/Status/</url>
  </view>
</hudson>

We can see that the job information is under the job element so the following code goes into the controller action. This code can be easily modified to work in a non ASP.net MVC environment.

        (...)
        Dim jenkinsURI As New Uri("http://myhost.com:8080/api/xml")
        Dim request As HttpWebRequest
        Dim xmlJobList As XElement
        model.failedJobs = New List(Of JenkinsJob)
        Try
            request = WebRequest.Create(jenkinsURI)
            Dim res As HttpWebResponse = request.GetResponse
            Dim reader As New StreamReader(res.GetResponseStream)
            xmlJobList = XElement.Parse(reader.ReadToEnd)
            reader.Close()
            For Each job In xmlJobList...<job>
                Dim c As String = job.<color>.Value
                If c = "red" Then
                    Dim jj As New JenkinsJob
                    jj.Name = job.<name>.Value
                    jj.URL = job.<url>.Value
                    jj.failed = True
                    model.failedJobs.Add(jj)
                End If
            Next
        Catch ex As Exception
            ' ignore for now
        End Try

        Return View(model)

It will loop through all the job nodes and if the color element is set to red then this indicates a filed job so add the name and URL to the view model to pass to the view.

This is the section of the view which will display the failed job. It uses the new bootstrap template that ships with Visual Studio 2013 and will only appear if the are any failed jobs.

    (...)
    @If Model.failedJobs.Count > 0 Then
        @<div class="col-md-3">
            <h2 class="alert-danger"><a href="http://myhost.com:8080/view/Status/" target="_blank" title="http://myhost.com:8080/view/Status/">Failed Jenkins Jobs</a></h2>
            <table cellpadding="2">
                <tr>
                    <th>Job Name</th>
                </tr>
                @For Each job In Model.failedJobs
                    @<tr>
                        <td><a href="@job.URL" title="@job.URL">@job.Name</a></td>
                    </tr>
                Next
            </table>
        </div>
    End If
    (...)

2013/06/24

VB.Net Getting a MD5 from a remote file asynchronously

Filed under: .NET, VB.NET — Tags: , , — vbmagic @ 3:10 pm

Took me a few hours to work this one out so I thought I’d post a small code snippet to show how it is done.

To do this you need the following Imports

Imports System.Net
Imports System.IO
Imports System.Security.Cryptography

It cheats in the fact that it downloads the file first and then does a checksum on it. But this is going to be used as a trigger for something else.

    Private _wc As New WebClient
    Private _baseUri As String = "http://mysite.com/myfiles/"

    Private Async Function GetDetails(filename As String) As Task(Of String)
        Await _wc.DownloadFileTaskAsync(New Uri(_baseUri & filename), "c:\downloads\" & filename)
        Dim result As String
        Using stream = New BufferedStream(File.OpenRead("c:\downloads\" & filename), 1200000)
            Dim hash As New MD5CryptoServiceProvider
            Dim checksum As Byte() = hash.ComputeHash(stream)
            result = BitConverter.ToString(checksum).Replace("-", String.Empty)
        End Using
        Return result
    End Function

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim md5 As String
        md5 = Await GetDetails("myfile.exe")
        ResultLabel.Text = md5
    End Sub

2013/03/25

Windows Phone 8 Speech API is easier than I thought

I decided to try out the Windows Phone 8 Speech API and expecting it to be quite complicated I had a pleasant surprise…

I created a new project and found that all the speech functionality is already in the SDK.

Below is a simple XAML screen to test out the functionality

        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="VBMAGIC" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock Text="speech toy" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <StackPanel>
                <TextBlock HorizontalAlignment="Center">Enter Text To Say</TextBlock>
                <TextBox x:Name="speechTextBox"></TextBox>
                <Button x:Name="sayitButton">Say It</Button>
                <Button x:Name="listenButton">Listen</Button>
            </StackPanel>
        </Grid>

And this is all the code I needed to actually handle everything.

Imports System
Imports System.Threading
Imports System.Windows.Controls
Imports Microsoft.Phone.Controls
Imports Microsoft.Phone.Shell
Imports Windows.Phone.Speech.Synthesis
Imports Windows.Phone.Speech.Recognition

Partial Public Class MainPage
    Inherits PhoneApplicationPage

    Private _synth As New SpeechSynthesizer
    Private _recog As New SpeechRecognizerUI()

    ' Constructor
    Public Sub New()
        InitializeComponent()

        SupportedOrientations = SupportedPageOrientation.Portrait Or SupportedPageOrientation.Landscape

    End Sub

    Private Sub sayitButton_Click(sender As Object, e As RoutedEventArgs) Handles sayitButton.Click

        ' Says it all
        SayIt()

    End Sub

    Private Async Sub listenButton_Click(sender As Object, e As RoutedEventArgs) Handles listenButton.Click

        _recog.Settings.ReadoutEnabled = False
        _recog.Settings.ShowConfirmation = False

        ' Display the speech recognition dialogue
        Dim recoResult = Await _recog.RecognizeWithUIAsync()

        ' Put the text that comes back into the speechTextBlock
        ' and then say it.
        speechTextBox.Text = recoResult.RecognitionResult.Text
        SayIt()
    End Sub

    Private Async Sub SayIt()

        ' Speak the contents of the speechTextBox
        Await _synth.SpeakTextAsync(speechTextBox.Text)

    End Sub
End Class

I switched off some of the default Speech Recognition UI features which were a bit of an overkill for this simple app (Look under _recog.Settings for all settings)

To enable this functionality to work you need to modify the app manifest. On VB.net projects. Click the Show All Files icon in the Solution Explorer, Open the My Project Directory, Double click the WMAppManifest.xml file, Click the capabilities Tab and make sure that the following options are ticked.

  • ID_CAP_MICROPHONE
  • ID_CAP_NETWORKING
  • ID_CAP_SPEECH_RECOGNITION

That is it. Nothing else required.

2013/02/06

The Spider is back. This time using VB.net

Filed under: .Net Micro Framework, Fez Spider, Gadgeteer, Glide — Tags: , , , — vbmagic @ 10:55 am

Over the last few days I’ve been gradually re-coding my Gadgeteer game from C# to VB.net now that the .Net Micro Framework 4.2 implementation is ready for the Spider.

That job is now finally over and the game now running as it was under the 4.1 framework.

Here is a little video of it running:

https://vimeo.com/59003253

The code for this project is available on the TinyCLR codeshare page.

I’ll try to work more on it when I get the spare time to do so.

2012/12/19

Plumbing in Place

Filed under: .NET, Learning, VB.NET — Tags: , , , , , — vbmagic @ 1:54 pm

Spent last weekend and some of the start of my Xmas holiday trying to finalise the plumbing between my three tiers.

There are now two types of services in the middle tier.

  1. OData read-only service. This is where things like the web client can get access to the game entities that get generated and allow for the game codex to be built. These will remain open and available to anyone that wants to view them.
  2. Updates and membership information. This will be a WCF based service mainly due to requiring the “Updates” to be sent over the service bus to the back end part of the system. And this is what will end up being secured at a later date when I’ve managed to research it more.

The only part of the feed currently available is the Latest News feed which is shown on the front page of the client web site currently.

There is also a hidden part of the client website which handles adding new news articles.

This is basically how adding a new news article works…

  • Client Website sends a news article to the middle tier WCF Service AddNewsArticle
  • The middle tier web service generates a GUID (Checking job result list in the DB to make sure it doesn’t already exists) and then creates a Service Bus “Job” which contains the GUID and passes GUID back to client.
  • The back end tier receives the news article job and then adds the article to the database and writes an entry in the job result list saying that it successfully created or it failed to create the news article using the GUID as a key.
  • While the back end is processing this job the web client has requested the job result from the middle service tier using the GUID key it recieved earlier. This service will wait up to thirty seconds for the job result to be generated (Checking every second) and then return the result back to the web client. It will also send a job to the back end to delete the job result.
  • The web client will either go back to the front page and the news article will then be displayed, or if there was an error, it will display the error on the add news article page so the user can decided what to do (I.e. me try to fix it 😉 )

The upshot all this recent work on the plumbing side of things means that I can now proceed and create the more interesting bits of the game and hopefully make very few changes to the way the plumbing works.

The heart of this system is the Job that passes from the Service Tier to the Back end Tier. All the code required to do a “Job of work” is held in these jobs. All the service tier has to do is create a job of a specific type and populate the required information, then send this job over the service bus to the back end. All the back end has to the is “Process” the job which makes the back end very small and simple in the way of code. All the work is done in the Job Process function. Hence most of the future development will be in these jobs and the client.

Onwards and upwards.

2012/12/12

Just a quick update … It begins

Filed under: VB.NET — Tags: , , , , — vbmagic @ 11:26 am

The OData feed now has it’s first collection. As an initial test I created a news feed to keep the website updated on progress. I’ve not done the code to update the news feed just yet as I’m investigating securing WCF Rest services with the Azure ACS (Planning to go through the Windows Identity Foundation Patterns: On-Premise and Cloud Pluralsight course to help with this)

But the website now consumes this feed by displaying the last 5 news articles on the front page:

https://tyranntrpg.org/

This is the code in the controller that accesses the service:

    Private Const CODEXURL As String = "https://tyranntrpg.org:8443/OData/Codex.svc"
    Private _codexContext As New TranntCodexServiceReference.GlobalDbContext(New Uri(CODEXURL))

    Function Index() As ActionResult
        Dim model As List(Of NewsArticle)

        model = (From a In _codexContext.NewsArticles
                Order By a.DateAdded
                Select a).Take(5).ToList

        Return View(model)
    End Function

And this is the code in the Razor View which displays it:

    <li>
        <h4>Latest News</h4>
        <ul>
            @For Each a In Model
                @<li>@a.DateAdded.Date.ToLongDateString : <b>@a.Subject</b>
                    <p>@a.Body</p>
                 </li>
            Next
        </ul>
    </li>

2012/12/10

Multiple Database Context Mess and Intercepting writes on a WCF Data Service

Filed under: .NET, Azure, VB.NET — Tags: , , , , , , , — vbmagic @ 3:00 pm

Hi,

Another weekend on hacking code to get the Tyrannt project off the ground. Again I am concentrating on the middle OData web service tier. One of the rules I set myself was to not allow this tier to update the database. These update requests are only meant to be done on the back end tier via messages passed along the Azure Service Bus (ASB).

After an initial hiccup which resulted in my WCF service request falling over with “Not enough memory” (These are hosted in Azure extra small compute instances). I managed to get a working service in the cloud that exposed all my current tables.

Next I wanted to split the tables over multiple data services. I initially achieved this by creating multiple database contexts. This also allowed me to intercept the SaveChanges call in the database context when someone did a Post or Put (Below is my current thinking of how to do this, although this may change when I find it doesn’t work 😉 )

    Public Overrides Function SaveChanges() As Integer
        For Each change In ChangeTracker.Entries
            Dim job As tJob
            Dim entity = change.Entity
            Dim entityType = change.Entity.GetType.Name
            Select Case entityType
                Case "NewsArticle"
                    job = New tjUpdateNewsArticle
                    Dim na As NewsArticle = CType(entity, NewsArticle)
                    '.... Etc
            End Select
        Next

        Return MyBase.SaveChanges()
    End Function

But when it came to trying to actually run this, I kept getting an error saying:

The model backing the 'TyranntSubsetContext' context has changed since the database was created.

After a lot of searching it seemed like some people said this was possible and other said it was not. I decided to change tack and use a single database context that had all my tables in it, but make a duplicate one in my service project which I can use to intercept the saves. (The Back end tier will need normal database access as this will be doing the writes)

Anyway I still needed to expose different tables in different services. And as a nice surprise this time, it was very easy to do.

Here is my DB Context class:

Imports System.Data.Entity
Imports Tyrannt.Model.Email
Imports Tyrannt.Model.News
Imports Tyrannt.Model.Errors
Imports Tyrannt.Model.Membership
Imports Tyrannt.Infrastructure.Jobs
Imports Tyrannt.Infrastructure.Jobs.News

Public Class GlobalDbContext
    Inherits DbContext

    Public Property NewsArticles As DbSet(Of NewsArticle)
    Public Property EmailMessages As DbSet(Of EmailMessage)
    Public Property ErrorMessages As DbSet(Of ErrorMessage)
    Public Property Members As DbSet(Of Member)
    Public Property MemberTypes As DbSet(Of MemberType)

    'Public Overrides Function SaveChanges() As Integer
    '    For Each change In ChangeTracker.Entries
    '        Dim job As tJob
    '        Dim entity = change.Entity
    '        Dim entityType = change.Entity.GetType.Name
    '        Select Case entityType
    '            Case "NewsArticle"
    '                job = New tjUpdateNewsArticle
    '                Dim na As NewsArticle = CType(entity, NewsArticle)

    '        End Select
    '    Next

    '    Return MyBase.SaveChanges()
    'End Function

End Class

And this is my service code (For now I only want to expose the NewsArticles one) :

Imports System.Data.Services
Imports System.Data.Services.Common
Imports System.Linq
Imports System.ServiceModel.Web

Public Class News
    Inherits DataService(Of GlobalDbContext)

    ' This method is called only once to initialize service-wide policies.
    Public Shared Sub InitializeService(ByVal config As DataServiceConfiguration)

        ' Expose only the required tables with the relevant access rights
        config.SetEntitySetAccessRule("NewsArticles", EntitySetRights.All)

        ' General settings
        config.UseVerboseErrors = True
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3
    End Sub

End Class

And it worked 🙂

For those curious, here is the service URI:

https://tyranntrpg.org:8443/OData/Codex.svc/

I cannot guarantee this service will always work or still exist in future but it’ll be there while I test client side code.

2012/11/22

I feel a project coming together

As a way of trying to put all the stuff that I am learning together. I’ve come up with a project. I have been attempting to write a game for ages and have decided to make it using all the new Microsoft technologies. (You may have seen some of the preliminary work resulting in blog posts here.)

So this project will be a web based RPG game (With mobile clients to come later hopefully). It will be hosted in Azure and it will make use of Web Roles, Worker roles, SQL Azure, Azure Service Bus/ACS and Azure Storage. It will be written in Visual Basic .Net.

This is the website: https://www.tyranntrpg.org/

The front page has more details on what is going on and I hope to update the progress regularly here on this blog.

The current site doesn’t do too much as most of the work is going on behind the scenes. But the Codex part will get updates as things progress. In the images section of the codex you can see some of the artwork done by http://www.battleaxegfx.com/
This is one of the images (It will eventually be the Something has gone wrong image)
Dragon and Warrior

This project has been going on for a few years using different tools to create hence the collection of artwork and ideas.

Hopefully this time I’ll get it finished 🙂

2012/11/19

Displaying Formatted XML in an ASP.net Razor Web Page

Filed under: .NET, ASP.NET MVC, VB.NET — Tags: , , , — vbmagic @ 4:10 pm

A quick post for those looking to display a formatted XML document on an ASP.net Razor web page. The following code is in the controller and used to create the string to get passed through to the view. This example is based on displaying the information pulled from the Azure Management API as mentioned in previous blogs. The data is stored in a SQL Server database.

    Function ShowRawXML(serviceOperationId As String, subscriptionName As String) As ActionResult
        Dim model As New ShowRawXMLViewModel

        Dim op = (From o In _db.ServiceOperations
                  Where o.ServiceOperationId = serviceOperationId And o.Subscription.FriendlyName = subscriptionName
                  Select o).Single

        model.RawXML = vbCr & Server.HtmlEncode(op.RawXML)
        model.SubscriptionName = subscriptionName
        model.SubscriptionID = op.Subscription.SubscriptionID
        model.ServiceOperationID = serviceOperationId
        model.ServiceOperationType = op.Type.Name

        Return View(model)
    End Function

In the view, we are going to use the @Html.Raw function so we used the Server.HtmlEncode function to make it safe to display in HTML. Next we get to the razor view…

@ModelType AzureManager.Domain.ShowRawXMLViewModel

@Code
    ViewData("Title") = "ShowRawXML"
End Code

<h2>Subscription ID [@Model.SubscriptionID] @Model.SubscriptionName</h2>

<h3>Show Raw XML for Service Operation @Model.ServiceOperationID</h3>
<h3>Operation Type: @Model.ServiceOperationType</h3>

<pre>
    @Html.Raw(Model.RawXML)
</pre>

<p>
    @Html.ActionLink("Back to List", "ServiceOperations", New With {.Id = Model.SubscriptionName})
</p>

Here you notice we display the raw XML inside a <pre></pre> tag. This will preserve the white space in the string. More information on this here: HTML <pre> Tag

2012/11/13

Parsing the HTTPResponse from the Azure Management API

Filed under: .NET, Azure, VB.NET — Tags: , , , , , — vbmagic @ 5:50 pm

Ok, after that last example I ended up pulling what hair I had left in my head out trying to parse the XML that got returned. I have come across this problem before but my memory being what it is I had totally forgotten about it so have written this blog post to help remind myself next time this happens.

I couldn’t seem to be able to get at the elements in the data. I was doing this:

            ' Parse the web response
            responseStream = httpResponse.GetResponseStream
            reader = New StreamReader(responseStream)
            Dim rawOutput As String = reader.ReadToEnd
            Dim xdoc As XDocument = XDocument.Parse(rawOutput)

            Dim serviceOperations = From s In xdoc.Descendants("SubscriptionOperation")
                                    Select s


            For Each s In serviceOperations
                Dim email = s.Descendants("UserEmailAddress").Value
                Dim operation = s.Descendants("OperationName").Value
                Dim opDate = s.Descendants("OperationStartedTime").Value
                Console.WriteLine(String.Format("User: {0} Did: {1} On: {2}", email, operation, opDate))
            Next

And stepping through the code I found that ServiceOperations resolved to nothing. After a couple of hours of poking and prodding and then searching the internet I came across the solution in this post: Fetching Hosting Services Name Using Windows Azure Management API

Namespaces! Memory starts ticking over and I remember hitting this before. So here is the revised code (In full) which will get Subscription details (Basically audit information) from the Management API

Imports System.Net
Imports System.Security.Cryptography.X509Certificates
Imports System.IO
Imports System.Text

Module Module1

    Sub Main()
        ' X.509 certificate variables
        Dim certStore As X509Store
        Dim certCollection As X509Certificate2Collection
        Dim certificate As X509Certificate2

        ' Request and response variables
        Dim httpRequest As HttpWebRequest
        Dim httpResponse As HttpWebResponse

        ' Stream variables
        Dim responseStream As Stream
        Dim reader As StreamReader

        ' URI variable
        Dim requestURI As Uri

        Try

            ' specify time range
            Dim startTime As String = "2012-11-11"
            Dim endTime As String = "2012-11-13"

            ' The ID for the Windows Azure subscription.
            Dim subscriptionId As String = "{Your Subscription}"

            ' The thumbprint for the certificate. This certificate would have been
            ' previously added as a management certificate within the Windows
            ' Azure management portal.
            Dim thumbPrint As String = "{Your Thumbprint}"

            ' Open the certificate store for the current user.
            certStore = New X509Store(StoreName.My, StoreLocation.CurrentUser)
            certStore.Open(OpenFlags.ReadOnly)

            ' Find the certificate with the specified thumbprint
            certCollection = certStore.Certificates.Find(
                X509FindType.FindByThumbprint,
                thumbPrint,
                False)

            ' close the certificate store
            certStore.Close()

            ' Check to see if mat
            If certCollection.Count = 0 Then
                Throw New Exception("No certificate found containing thumbprint " & thumbPrint)
            End If

            ' A matching certificate was found.
            certificate = certCollection(0)
            Console.WriteLine("Using certificate with thumbprint: " & thumbPrint)

            ' create new request
            requestURI = New Uri(
                String.Format("https://management.core.windows.net/{0}/operations?StartTime={1}&EndTime={2}",
                              subscriptionId, startTime, endTime)
                )

            httpRequest = HttpWebRequest.Create(requestURI)

            ' add certificate to requrest
            httpRequest.ClientCertificates.Add(certificate)

            ' Specify the version information in the header
            httpRequest.Headers.Add("x-ms-version", "2012-03-01")
            httpRequest.ContentType = "application/xml"

            ' Make the call using the web request
            httpResponse = httpRequest.GetResponse

            ' Display the response status code
            Console.WriteLine("Response status code: " _
                              & httpResponse.StatusCode)

            ' Display thr request ID returned by windows azure
            If httpResponse.Headers IsNot Nothing Then
                Console.WriteLine("x-ms-request-id: " & httpResponse.Headers("x-ms-request-id"))
            End If

            ' Parse the web response
            responseStream = httpResponse.GetResponseStream
            reader = New StreamReader(responseStream)
            Dim rawOutput As String = reader.ReadToEnd
            Dim ns As XNamespace = "http://schemas.microsoft.com/windowsazure"

            Dim xdoc As XDocument = XDocument.Parse(rawOutput)

            Dim serviceOperations = From s In xdoc.Descendants(ns + "SubscriptionOperation")
                                    Select s


            For Each s In serviceOperations
                Dim email = s.Descendants(ns + "UserEmailAddress").Value
                Dim operation = s.Descendants(ns + "OperationName").Value
                Dim opDate = s.Descendants(ns + "OperationStartedTime").Value
                Console.WriteLine(String.Format("User: {0} Did: {1} On: {2}", email, operation, opDate))
            Next

            httpResponse.Close()
            responseStream.Close()
            reader.Close()
            Console.ReadKey()
        Catch ex As Exception
            Console.WriteLine("Error encountered: " & ex.Message)
            Console.ReadKey()
            System.Environment.Exit(1)
        Finally
            System.Environment.Exit(0)
        End Try
    End Sub

End Module

This can be used as an example to pull audit information from Azure. I hope this helps others not make my silly mistake 😉

P.s. I hope you like the new theme. Thanks to the above blog post (Debugmode.net) for showing it to me. It makes reading source code much better on wider monitors.

Older Posts »

Blog at WordPress.com.