Archive for October, 2006

31OctCastle and workflow foundation

Yesterday I had to implement workflow in an application.  I had never used it before, and kind of feared that it was going to take me a couple of days to get the hang of it.

Well 2 hours later I had my test workflow going so it isn’t hard at all. If I can do it in that amount of time I’m sure many of you can beat me.

Now I wanted to host the workflow runtime in my container and this is how I went about it:

I made 2 wrapper properties in a settings class :

 

 public static class SeshatAppSettings

    {

        public static WorkflowRuntime WorkflowRuntime

        {

            get

            {

                return HttpContext.Current.Application["WorkflowRuntime"] as WorkflowRuntime;

            }

            set

            {

                HttpContext.Current.Application["WorkflowRuntime"] = value;

            }

        }

 

        public static ManualWorkflowSchedulerService WorkflowScheduler

        {

            get

            {

                return WorkflowRuntime.GetService(typeof(ManualWorkflowSchedulerService)) as ManualWorkflowSchedulerService;

            }

        }

 

 

    }

The next thing that comes to mind is, hey I need to start this when my application starts (start, app starts == IStartable). Because the workflow is hosted in asp.net we need a persistence medium. I chose to stay with the standard workflow foundation sql persistence service. And that implementation goes a bit like this :

public class WorkflowHost : IStartable

    {

 

        #region IStartable Members

 

        ///

        /// Starts this instance.

        ///

        public void Start()

        {

            // Create an instance of the workflowRuntime

            WorkflowRuntime workflowRuntime = new WorkflowRuntime();

 

            // Add a manual scheduling service

            ManualWorkflowSchedulerService manualService = new ManualWorkflowSchedulerService();

            workflowRuntime.AddService(manualService);

 

            // Add our persistence

            AddPersistenceService(workflowRuntime);

 

            // Start the workflow runtime

            workflowRuntime.StartRuntime();

 

            // Store it in a place so that the whole application can get to it.

            SeshatAppSettings.WorkflowRuntime = workflowRuntime

        }

 

        ///

        /// Stops this instance.

        ///

        public void Stop()

        {

            WorkflowRuntime workflowRuntime = SeshatAppSettings.WorkflowRuntime;

            workflowRuntime.StopRuntime();

 

        }

 

        #endregion

 

 

        private void AddPersistenceService(WorkflowRuntime runtime)

        {

            // Create the SqlWorkflowPersistenceService.

            string connectionString = “Initial Catalog=WorkflowPersistenceStore;Data Source=localhost;Integrated Security=SSPI;”;

            bool unloadOnIdle = true;

            TimeSpan instanceOwnershipDuration = TimeSpan.MaxValue;

            TimeSpan loadingInterval = new TimeSpan(0, 2, 0);

            SqlWorkflowPersistenceService persistService = new SqlWorkflowPersistenceService(connectionString, unloadOnIdle, instanceOwnershipDuration, loadingInterval);

 

            // Add the SqlWorkflowPersistenceService to the runtime engine.

            runtime.AddService( persistService );

 

        }

    }

The last bit we need to take care of is adding our startable component to the container which is pretty easy to do.

 

public class WorkflowFacility : AbstractFacility

    {

        protected override void Init()

        {

            Kernel.AddComponent(“workflow.defaultHost”, typeof(IStartable), typeof(WorkflowHost));

        }

    }

And the final piece is the configuration from the component

 

   

28OctIf you ever need intellisense for asp.net projects

Then these guys would be a good place to start.

http://www.bluevisionsoftware.com/WebSite/Products…

27OctI am looking for a junior developer

Please spread the word :)

http://www.seek.co.nz/jobsearch/index.ascx?DateRan…

I  want somebody with almost no experience but a strong passion for the internet.

This person will have to move to New Plymouth.

If you know somebody please give them my details.

25OctGet intellisense for Base4 to edit schema’s

I don’t know how many of you know about this but I certainly didn’t

When you download the source of base4 which you can find here

And you navigate to the folder C:\Program Files\Base4 Solutions Ltd\Base4 version 2.1\Source\Base4.Storage\Resources

Or whereever you installed the source and copy the xsd file that is there to the folder C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas (the folder xml schemas in your visual studio install dir)

When you restart/start visual studio now and you want to edit a schema now the things popup to assist you

Don’t forget to register on the base4 site if you haven’t already.

 

Enjoy

19OctUbuntu linux delivers great value

Yesterday evening I installed Ubuntu linux because i want something where i can run mono on that is not running on a windows OS. Lately I’ve got this thing that I want my apps to work cross-platform etc. So I’ve been looking into linux type of OS’es.

The two I like are suse and ubuntu now. Don’t really care much about redhat never have.

In my life as an IT professional I’ve worked with several linux distributions. I don’t know if mandrake is still around but those guys were my first linux installation for firewall implementations, until they got the idea to make their software not free anymore.

Anyway back to ubuntu. It isn’t mac os but it is the easiest linux I’ve used so far. It took me about 30 minutes to install it on my virtual server. And it took anoter 10 to get mono installed on it.

The experience is a whole lot better than having to fiddle all over the place with settings and conf files.

Also I didn’t get the questions about root etc.. Yes this distibution is streamlined and works properly.

And now I finally have a real operating system to put on the iMac that runs on our network. Because MacOS 9 isn’t really networking and Ajax friendly.  The only browser that runs on that iMac is iCab and if you have some experience with iCab then you’ll know that that is not really worth calling a browser.

You can run download an alternate distro of ubuntu that runs on the iMac architecture.

Yes another thing there were only a couple downloads to chose from not the usual list where you almost need to know your motherboard type to get the right version down.

Below is a screenshot. Look at the folder list on the left. It’s not your usual confusing bit but this time it has got real indications of what you want to do. :)

This doesn’t mean I will uninstall Vista and start working on linux from now on but I’m happy about the fact that I don’t have to deal with Mac OS 9 anymore on the intranet for the browser application.

19OctGlobat.com tactics scandalous

First off I am not going to link to them in this post because that would be like free advertising.

A couple of months ago I took 2 domainname accounts with them and ever since they have been sending me emails every couple of days telling me i need to upgrade my account to some super hosting plan they provide.

I don’t need the friggin hostiing because I have my own server running.
Their constant emailing has made me classify them into a folder that doesn’t show up in my unread mails.

So the latest tactic, which can be called dodgy at the very least, is to send an email about an automatic upgrade and if you fail to opt-out you get a hosting account @ 40USD/month

The letters they sent (I won’t be posting all the other mails of course) :

1st e-mail :

Dear Ivan,

We would like to extend an incredible money-saving offer for medlr.com to thank you for your continued business. In 10 days from this date, we will be providing you with an upgrade to our new TeraByte Pro(tm) Hosting package at the low cost of just $39.95!

This amazing promotion is part of our new special upgrade program of hosting services for our loyal customers. The feedback we received about the upgrade program has been overwhelming. We heard from many of our customers who learned about these upgrade promotions too late or through a friend. Many customers then suggested that we utilize an automatic upgrade process along with some form of an additional incentive to initiate these upgrades. Well, we have listened to your feedback and we are ready to act!

= = = Take Your Domain to the Next Level = = =

As our dedicated domain customer, you will be receiving an automatic upgrade to Globat.com’s award-winning hosting services at a highly-discounted price of only $39.95, instead of $95.40! That breaks down to only $3.33 per month, truly a price that won’t break the bank!

2nd e-mail: (medlr.com has nothing on it, don’t follow)

Dear Ivan,

Congratulation! As per our previous notices, today we have upgraded your hosting account for www.medlr.com to our all new Terabyte Pro™ level and processed your invoice 3629260 in the amount of $39.95 through your credit card account on file.

You are now utilizing Globat.com’s industry-leading TeraByte Plus Hosting(TM) solution! With this upgrade you have also locked in your web hosting pricing for as long as your account remains in this program. You have also qualified to receive one free month of web hosting. To claim your free month please go to:

https://order.globat.com/newOrder/nop_givemonth.pl

and enter the following coupon code

FREEMONTH

into the field provided. Then hit the “Add My Free Month” button. Upon completion of this step your free month will automatically be added to your current account renewal date and you can view your next renewal date at:

https://login.globat.com/cp2/account.php

or view your receipt for this transaction at:

https://login.globat.com/cp2/account.php?page=invoices

 

My response in as much words as i want to give them.

> Excuse me I DON’T want anything I DID NOT EXPLICITLY ORDER.
> Please cancel your super promotion. I’ll move my domain names because
> I think these tactics are scandalous.
>
> I’ll start blogging about it too.. What type of gangsters are you guys.
> Where is it legal that I buy stuff because I did not tell you i didn’t  want it.

 

By the time I had finished this email. They already came back to me telling me my money has been refunded.
I’m going to post it anyway because I do think that if that is the direction we are heading then I think nobody will every be safe. Becauwe whenever you buy something somewhere it gives the seller the right to sell you more stuff without even approving the buy.

Well, it feels good to get that of my chest. I’m wondering where these globat people won their award, must be in the gangster syndicate or something, maybe they got it from the Soprano family or something.

18OctBase4 and Castle… the controller

The next bit for pages in general is that often they represent a list. Often this list is a really long list that you’d like to page through.

I put this functionality into a generic base controller class. The whole idea of my set up is to provide a technique called progressive enhancement of forms.  In short you create an application that works completely without javascript using hyperlinks for gets and submit buttons etc.  Then you attach some css selector classes to the different bits of functionality and you attach these behaviors to the html elements on your page.

I thought that that is a really nice way of enriching the experience of people that allow it to be enriched ;)   And that is the explanation of the ajaxCall bool variable on some of the methods

The first thing is the interface for the class :

using System;

namespace NBlogr.Presenter

{

    public interface INBlogrController

    {

        bool IsCompositeView{get;}

        void Delete(T item, int pageNumber, int pageSize);

        void Delete(T item, int pageNumber, int pageSize, bool ajaxCall);

        void Edit(Guid Id);

        void Edit(Guid Id, bool ajaxCall);

        void JumpToPage(int pageNumber, int pageSize);

        void JumpToPage(int pageNumber, int pageSize, bool ajaxCall);

        void List();

        void List(bool ajaxCall);

        void NavigateBack(int pageNumber, int pageSize);

        void NavigateFirst(int pageSize);

        void NavigateNext(int pageNumber, int pageSize);

        void NavigateBack(int pageNumber, int pageSize, bool ajaxCall);

        void NavigateFirst(int pageSize, bool ajaxCall);

        void NavigateNext(int pageNumber, int pageSize, bool ajaxCall);

        void Show();

        void Show(bool ajaxCall);

        void Save(T item, int pageNumber, int pageSize);

        void Save(T item, int pageNumber, int pageSize, bool ajaxCall);

        void CreateNew();

        void CreateNew(bool ajaxCall);

    }

}

And the next thing is the base class again :

using System;

using System.Collections.Generic;

using System.Text;

using Base4.Storage;

 

namespace NBlogr.Presenter.Abstract

{

    public abstract class AbstractAdminController : AbstractController, INBlogrController where T: class, IItem, new()

    {

        bool compositeView;

        int _pageSize;

 

        public AbstractAdminController()

            : base()

        {

            this.compositeView = false;

            _pageSize = 10;

        }

 

        public AbstractAdminController(bool compositeView) : base()

        {

            this.compositeView = compositeView;

            _pageSize = 10;

        }

 

        internal abstract void getList(int pageNumber, int pageSize, bool skipLayout);

 

 

        internal void getList(int pageNumber, int pageSize)

        {

            getList(pageNumber, pageSize, false);

 

        }

        #region INBlogrController Members

 

        public bool IsCompositeView

        {

            get

            {

                return compositeView;

            }

        }

        public int PageSize

        {

            get

            {

                return _pageSize;

            }

            set

            {

                _pageSize = value;

            }

        }

 

        public void Delete(T item, int pageNumber, int pageSize)

        {

            Delete(item,pageNumber,pageSize,false);

        }

 

        public abstract void Delete(T item, int pageNumber, int pageSize, bool ajaxCall);

 

        public void CreateNew()

        {

            CreateNew(false);

        }

 

        public virtual void CreateNew(bool ajaxCall)

        {

            Edit(Guid.NewGuid(), ajaxCall);

        }

 

        public void Edit(Guid Id)

        {

            Edit(Id, false);

        }

 

        public abstract void Edit(Guid Id, bool ajaxCall);

 

        public void JumpToPage(int pageNumber, int pageSize)

        {

            getList(pageNumber, pageSize);

        }

 

        public virtual void JumpToPage(int pageNumber, int pageSize, bool ajaxCall)

        {

            getList(pageNumber, pageSize, ajaxCall);

        }

 

        public void List()

        {

            getList(1, _pageSize);

        }

 

        public virtual void List(bool ajaxCall)

        {

            getList(1, _pageSize, ajaxCall);

        }

 

        public void NavigateBack(int pageNumber, int pageSize)

        {

            getList(–pageNumber, pageSize);

        }

 

        public void NavigateFirst(int pageSize)

        {

            getList(1, pageSize);

        }

 

        public void NavigateNext(int pageNumber, int pageSize)

        {

            getList(++pageNumber, pageSize);

        }

 

        public virtual void NavigateBack(int pageNumber, int pageSize, bool ajaxCall)

        {

            getList(–pageNumber, pageSize, ajaxCall);

        }

 

        public virtual void NavigateFirst(int pageSize, bool ajaxCall)

        {

            getList(1, pageSize, ajaxCall);

        }

 

        public virtual void NavigateNext(int pageNumber, int pageSize, bool ajaxCall)

        {

            getList(++pageNumber, pageSize, ajaxCall);

        }

 

        public void Show()

        {

            getList(1, _pageSize);

        }

 

        public virtual void Show(bool ajaxCall)

        {

            getList(1, _pageSize, ajaxCall);

        }

 

 

        public void Save(T item, int pageNumber, int pageSize)

        {

            Save(item, pageNumber, pageSize, false);

        }

 

        public abstract void Save(T item, int pageNumber, int pageSize, bool ajaxCall);

 

 

        #endregion

    }

}

Now onto the concrete implementation of my controller class

 

using System;

using System.Collections.Generic;

using System.Text;

using NBlogr.Core;

using NBlogr.Service;

using Base4.Storage;

using Castle.MonoRail.Framework;

using NBlogr.Common.Base4Integration;

using Castle.Services.Transaction;

 

namespace NBlogr.Presenter

{

 

    public class BlogController : Abstract.AbstractAdminController<Blog>

    {

        BlogService blogService;

        

        public BlogController(IService<Blog> blogService)

            : base(true)

        {

            this.blogService = blogService as BlogService;

             

             

        }

 

        private void internalGetList(int pageNumber, int pageSize)

        {

            int pageCount;

 

            IItemList<Blog> blog = blogService.GetAll(“Name”, pageNumber, pageSize, out pageCount);

 

            internalMapProperties(blog , pageNumber,pageSize, pageCount);

        }

 

        private void internalMapProperties(IItemList<Blog> blogs, int pageNumber, int pageSize, int pageCount)

        {

            int totalCount = blogs.Count;

 

            if (PropertyBag["blog"] == null)

                PropertyBag.Add(“blog”, new Blog());

            

            PropertyBag.Add(“blogs”, pageNumber);

            PropertyBag.Add(“index”, pageNumber);

            PropertyBag.Add(“count”, totalCount);

            PropertyBag.Add(“pageCount”, pageCount);

            PropertyBag.Add(“pageSize”, pageSize);

        }

 

        internal override void getList(int pageNumber, int pageSize, bool skipLayout)

        {

 

            internalGetList(pageNumber, pageSize);

 

            if (!skipLayout)

                RenderView(“Show”, false);

            else

                RenderView(“List”, true);

        }

 

 

 

 

        public override void Edit(Guid Id, bool ajaxCall)

        {

            Blog blog = blogService.GetById(Id);

 

            PropertyBag.Add(“blog”, blog);

             

            if (!ajaxCall)

            {

                getList(1, 0);

            }

            else

                RenderView(“Save”, ajaxCall);

 

        }

 

        public override void Delete([DataBind("blog")] Blog item, int pageNumber, int pageSize, bool ajaxCall)

        {

            blogService.Delete(item);

 

            if (!ajaxCall)

            {

                getList(1, 0);

            }

            else

                RenderText(getJsonResponse(“Contract deleted successfully”));

 

        }

 

        public override void Save([DataBind("blog")] Blog item, int pageNumber, int pageSize, bool ajaxCall)

        {

            PropertyBag.Add(“blog”, blogService.Save(item));

 

            if (!ajaxCall)

            {

                getList(1, 0);

            }

            else

                RenderText(getJsonResponse(“Contract saved successfully”));

        }

 

        private string getJsonResponse(string message)

        {

            return “{result: true, content: ‘” + message + “‘}”;

        }

 

    }

}

17OctBase4 and Castle … The service

UPDATE: THE SERVICE CLASS HAS CHANGED AGAIN.
Scratch the update.. it assumes that the object is already in the database which isn’t always the case. For this type of synchronisation to work I need some more time. So I reverted this back to the old version

This post continues my previous posts on using base4 and castle together.

I’m sorry but there is no Active record in the base4 way of doing things.

The previous posts you would be intrested in can be found here :

Hosting base4 inside your web application
Base4 and Castle continued… The facility

Today I want to show you the abstract service class that I use so the controller only talks to the service and never to the dataobject directly. 

It is not a very big class but i found it pretty useful.  As it turns out almost all my service classes in a previous project started to look very much alike so I grouped that functionality in an abstract class. There is one thing you have to look out for and that is if you have something like audit fields (Created, LastModified,…..) you need to first get the object from the datastore and then set just the changed properties on the retrieved object.

The first thing you need is the contract/interface for the service. I expect this interface to grow over time so keep checking NBlogr for updates but this where I am at now :)

using System;

namespace NBlogr.Service

{

    public interface IService

    where T : class, Base4.Storage.IItem, new()

    {

        void Delete(T item);

        Base4.Storage.IItemList GetAll(string sortExpression, int pageNumber, int pageSize, out int pageCount);

        T GetById(Guid Id);

        T Save(T item);

    }

}

And here’s the abstract class :

using Base4.Storage;

using Castle.Core.Logging;

using NBlogr.Common.Base4Integration;

 

using User = NBlogr.Core.User;

using Castle.Services.Transaction;

using Base4.Storage.Utils;

 

namespace NBlogr.Service.Abstract

{

 

    [Transactional]

    public abstract class AbstractService : IService where T : class,IItem,new()

    {

        internal readonly ILogger logger;

        internal IDataObject dao;

 

 

        public AbstractService(ILogger logger, IDataObject dao)

        {

            this.logger = logger;

            this.dao = dao;

        }

 

        [Transaction(TransactionMode.Requires)]

        public virtual T Save(T item)

        {

            return dao.Save(internalPrepareSave(item));

        }

 

        internal abstract T internalPrepareSave(T item);

 

        [Transaction(TransactionMode.Requires)]

        public virtual void Delete(T item)

        {

            dao.Delete(item);

        }

 

        public virtual IItemList GetAll(string sortExpression, int pageNumber, int pageSize, out int pageCount)

        {

            return dao.FindAll(sortExpression, pageNumber, pageSize, out pageCount);

        }

 

        public virtual T GetById(Guid Id)

        {

            return dao.GetById(Id);

        }

 

 

    }

}

And the last bit could be an implementation of this service.  I don’t have companies in NBlogr. I took this class from another project :

using System;

using Castle.Services.Transaction;

using Castle.Core.Logging;

using NBlogr.Common.Base4Integration;

using NBlogr.Core;

 

namespace NBlogr.Service

{

    [Transactional]

    public class BlogService : Abstract.AbstractService<Blog>

    {

 

        public BlogService(ILogger logger, IDataObject<Blog> dao)

            : base(logger, dao)

        {

 

        }

 

        internal override Blog internalPrepareSave(Blog item)

        {

            Blog itemToSave = GetById(item.Id);

 

            if (!itemToSave.Internals.InDatabase)

            {

                itemToSave.Created = DateTime.Now;

                itemToSave.Creator = CurrentUser;

            }

 

            itemToSave.AkismetKey = item.AkismetKey;

            //TODO: finish mapping properties

 

            return itemToSave;

        }

    }

}

This concludes the service for use with base4.  I hope you find it as useful as I do.
If somebody knows improvements etc. Please let me know I am always keen to learn.

I also realise that I can just call save on an IItem object but i like every tier to have it’s specific task so the dataobject doesn’t do much else than call IItem.Save() for now

16OctBase4 on Castle continued… The facility

Much of what I’m going to show today has been borrowed from Alex Henderson on the storage facility.

This post continues the hosting base4 inside your web application post

To integrate base4 in castle you can register either components or create a facility which allows it to hook into some bits of the castle life-cycle.

It doesn’t make such a big difference when actually writing code except for the fact that you have transactions managed by castle, caching etc practically for free.

The ObjectTransaction that is available in base4 is built on the transaction scope and uses TransactionOptions.Required. Translated freely this means it will attach itself to an existing transaction if one exists if not it will create one.

So below I’ll first put the code for the storage facility and the base4TransactionManager and the configuration in the config files.  The full version of these files and the classes can be found in the source control repository of NBlogr

The storage facility

namespace NBlogr.Common.Base4Integration

{

    public class Base4StorageFacility : AbstractFacility

    {

        protected override void Init()

        {

            // If no context has been set yet (but should be done in application_start) set the default context.

            if (StorageContext.Default == null)

            {

                string base4Context = FacilityConfig.Attributes["base4Context"];

 

                if (string.IsNullOrEmpty(base4Context))

                {

                    throw new StorageException(“The Base4StorageFacility requires a \”base4Context\” attribute to be set”);

                }

 

                StorageContext.SetDefault(base4Context);

            }

 

            // Add the transactionmanager to the registered components

            Kernel.AddComponent(“base4.transactionManager”, typeof(ITransactionManager), typeof(Base4TransactionManager));

 

            // Add the IItemcontext to the registered components

            Kernel.AddComponentInstance(“base4.defaultContext”, typeof(IItemContext), StorageContext.Default);

 

            // Add the base4Dataobject

            Kernel.AddComponent(“base4.dataObject”, typeof(IDataObject<>), typeof(BaseDataObject<>));

 

        }

    }

}

 

 

The transaction manager

 

 

namespace NBlogr.Common.Base4Integration

{

    [PerThread]

    public class Base4TransactionManager : DefaultTransactionManager

    {

    }

}

 

The IDataObject interface

 

 

public interface IDataObject : IBaseDataObject

    where T : class, IItem, new()

    {

        void Delete(T item);

        Base4.Storage.IItemList Find(ObjectPath oPath, string sortExpression);

        Base4.Storage.IItemList Find(string oPath, string sortExpression, int pageNumber, int pageSize, out int pageCount);

        Base4.Storage.IItemList Find(ObjectPath oPath, string sortExpression, int pageNumber, int pageSize, out int pageCount);

        Base4.Storage.IItemList Find(string oPath);

        Base4.Storage.IItemList Find(ObjectPath oPath);

        Base4.Storage.IItemList Find(string oPath, string sortExpression);

        Base4.Storage.IItemList FindAll(string sortExpression, int pageNumber, int pageSize, out int pageCount);

        Base4.Storage.IItemList FindAll(string sortExpression);

        Base4.Storage.IItemList FindAll();

        Base4.Storage.IItemList FindById(Guid Id);

        T GetById(Guid Id);

        T GetOne(string oPath, params object[] parameters);

        T GetOne(string oPath);

        T GetOne(ObjectPath oPath);

        T GetOneUsingSQL(string SQL);

        T GetOneUsingSQL(string SQL, ObjectScope scope);

        T GetOne(string opath, ObjectScope scope);

        T GetOne(ObjectPath path, ObjectScope scope);

        T Save(T item);

        string SortExpression { get; set; }

        void DeleteAll();

        void Delete(ObjectPath path);

        void Delete(string path);

        void Delete(string path, params string[] replaces);

        IItemList Find(ObjectPath path, ObjectScope scope);

        IItemList FindUsingSQL(string SQL);

        IItemList FindUsingSQL(string SQL, ObjectScope scope);

        IItemList Find(ObjectPath path, ObjectScope scope, string sortExpression, int pageNumber, int pageSize, out int pageCount);

        IItemList FindAll(ObjectScope scope, string sortExpression, int pageNumber, int pageSize, out int pageCount);

    }

 

The facilities.config file

 

 

<configuration>

 

  <facilities>

   

16OctNo post yesterday I was moving from team foundation to subversion

I was using Team foundation workgroup edition  for the past year to manage collaborating with a couple of people. And the other stuff you use team foundation for.

I like Team foundation a lot but unfortunately am not rich enough to buy licenses for everybody that needs to log bugs etc.  To avoid all those licensing issues we have switched over to a combination of open source packets.The total cost of the operation is about 450NZD for unlimited licenses.

So what is the setup :

The whole experience took about 6 hours to configure ant that is because I didn’t know apache at all and getting it to work with SSL did take me some time until I found the install guide that had a link to compiled binaries. I’m not that good in compiling ansi-C on a windows 2k3 64-bit machine without the windows sdk installed. Not to mention all these little tools you have to download all over the place. Most of them i had never heard of before, so yes I was happy to find that guide.

We are now completely switched over, except for the work items that are currently in team foundation server. It was a good moment to do this because the most of the previous projects have come to an end and the new wave is rolling in. I got really tired of retyping the emails etc I got into the issue tracker, simply becasue I couldn’t afford it.

Recent Flickrs

    Blogroll

    Recent Listening

    Scrobbler