Monday, February 16, 2009

More PowerShell Adventures

The journey to administration of IIS 7 on Windows Web Server 2008 R2 Core has not been a happy time. Hopefully perseverance will eventually pay some dividends. So my objective right now is to install the IIS 7 PowerShell snap-in. I know where it is: http://66.129.69.178/downloads/files/powershell/iis7psprov_x64_rc1.msi. I just need to download it to my server. Note my server doesn't currently have a DNS entry which means providing a hostname is just not going to work, I need to replace www.iis.net with the equivalent IP Address 66.129.69.178. Google throws up a number of wget scripts; after trying various versions and failing to get anything to retrieve the file I went back to basics and used a page from B#.Net Blog. This uses .Net Framework class System.Net.WebClient to retrieve the file. The problem with most of the scripts I found is that error reporting is generally left to the default PowerShell Exception report which by and large is not particularly useful:



Exception calling "DownloadFile" with "2" argument(s): "An exception occurred during a WebClient request."
At line:1 char:21
+ $client.DownloadFile <<<< ($url, $file)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

In order to get a little more detail I need to trap the exception and delve into it to find out what caused it. This is relatively straight forward using the trap mechanism. To begin with lets display the methods available on the Exception object.



PS C:\Users\Administrator> trap { $error[0].Exception | get-member } $client.DownloadFile($url,$file)

TypeName: System.Management.Automation.MethodInvocationException

Name MemberType Definition
---- ---------- ----------
Equals Method System.Boolean Equals(Object obj)
GetBaseException Method System.Exception GetBaseException()
GetHashCode Method System.Int32 GetHashCode()
GetObjectData Method System.Void GetObjectData(SerializationInfo info, StreamingContext c...
GetType Method System.Type GetType()
ToString Method System.String ToString()
Data Property System.Collections.IDictionary Data {get;}
ErrorRecord Property System.Management.Automation.ErrorRecord ErrorRecord {get;}
HelpLink Property System.String HelpLink {get;set;}
InnerException Property System.Exception InnerException {get;}
Message Property System.String Message {get;}
Source Property System.String Source {get;set;}
StackTrace Property System.String StackTrace {get;}
TargetSite Property System.Reflection.MethodBase TargetSite {get;}
Exception calling "DownloadFile" with "2" argument(s): "An exception occurred during a WebClient re
quest."
At line:1 char:63
+ trap { $error[0].Exception | get-member } $client.DownloadFile <<<< ($url,$file)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

The ToString method is reasonably useful but generates a fair amount of output, including a full stack trace. Time to see what it looks like:



PS C:\Users\Administrator> $client = new-object System.Net.WebClient
PS C:\Users\Administrator> $url = "http://66.129.69.178/downloads/files/powershell/iis7psprov_x64_rc1.msi"
PS C:\Users\Administrator> $file = Join-Path $home Downloads\iis7psprov_x64_rc1.msi
PS C:\Users\Administrator> $file
C:\Users\Administrator\Downloads\iis7psprov_x64_rc1.msi
PS C:\Users\Administrator> trap { $error[0].Exception.ToString() } $client.DownloadFile($url,$file)
System.Management.Automation.MethodInvocationException: Exception calling "DownloadFile" with "2" a
rgument(s): "An exception occurred during a WebClient request." ---> System.Net.WebException: An ex
ception occurred during a WebClient request. ---> System.Configuration.ConfigurationErrorsException
: Error creating the Web Proxy specified in the 'system.net/defaultProxy' configuration section. --
-> System.DllNotFoundException: Unable to load DLL 'rasapi32.dll': The specified module could not b
e found. (Exception from HRESULT: 0x8007007E)
at System.Net.UnsafeNclNativeMethods.RasHelper.RasEnumConnections(RASCONN[] lprasconn, UInt32& l
pcb, UInt32& lpcConnections)
[full stack trace elided]
at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Arra
y input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)
Exception calling "DownloadFile" with "2" argument(s): "An exception occurred during a WebClient re
quest."
At line:1 char:61
+ trap { $error[0].Exception.ToString() } $client.DownloadFile <<<< ($url,$file)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

As I said, a fair amount of output. But it does contain some useful information - Error creating the Web Proxy specified in the 'system.net/defaultProxy' configuration section. ---> System.DllNotFoundException: Unable to load DLL 'rasapi32.dll'. I guess I should have read the release notes:



If you run applications that use managed code that communicates with the Internet with autoproxy
detection, the operation will fail with an unhandled exception that mentions an error creating the
Web proxy because Rasapi32.dll could not be found.

To correct this, open the Machine.config file (default location is
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\CONFIG), locate the final closing </configuration>
tag, and append the following:

<system.net> <defaultProxy> <proxy usesystemdefault="false" proxyaddress="<replace_with_proxy_address>"
bypassonlocal="true" /> </defaultProxy> </system.net>

where <replace_with_proxy_address> is the address and port, if needed, of the proxy server
used by the client application to access the .NET application. For example, proxyaddress="<http://proxyserver:80>".

I'm not actually using a proxy server, what I want to do is avoid the system trying to load the rasapi32.dll. What I need to do is somehow indicate that I don't wish to use the default proxy, whatever that is defined as, when I'm making my WebClient.DownloadFile() request. The correct sequence of calls is then:



PS C:\Users\Administrator> $client = new-object System.Net.WebClient
PS C:\Users\Administrator> $url = "http://66.129.69.178/downloads/files/powershell/iis7psprov_x64_rc1.msi"
PS C:\Users\Administrator> $file = Join-Path $home Downloads\iis7psprov_x64_rc1.msi
PS C:\Users\Administrator> [System.Net.GlobalProxySelection]::Select = [System.Net.GlobalProxySelection]::GetEmptyWebProxy()
PS C:\Users\Administrator> trap { $error[0].Exception.ToString() } $client.DownloadFile($url,$file)

This next step ends up just being stupid, annoying, insulting, you get the picture, not happy. Installing the IIS 7.0 PowerShell Snap-in effectively says "go get the installer and run the installer". So I've finally got the installer. I go ahead and run the installer and the installation fails with an error IIS Version 7.0 or greater is required to install Microsoft Windows PowerShell provider for IIS 7.0. I'm running Windows Server 2008 r2, I've got IIS 7 installed. A forum post adds fuel to the fire, particularly this post:



This is "by design". What you have in Win7 beta is practically the same code as RC of powershell snapin, which is for downlevel versions only.

Really sucks. If something like this is "by design" then it should be documented somewhere obvious. This is just a waste of time for anyone who is trying to get enthusiastic about using this stuff. The workaround is also documented in the thread (thanks 13xforever), change minor version of IIS from 5 to 0 in HKLM:\SOFTWARE\Microsoft\InetStp (and back again after the installation is complete). Now I need to let PowerShell run scripts:



PS C:\Users\Administrator> Get-ExecutionPolicy
Restricted
PS C:\Users\Administrator> Set-ExecutionPolicy RemoteSigned

And to make my life easier on Core I've created $home\Bin\IIS.cmd containing the Target: for the IIS PowerShell Management Console menu item shortcut installed with the snap-in. Unfortunately I can't post that information because blogger thinks I'm doing something dodgy and removes the offending code and the rest of the post (I guess if you've read this far you might consider that a bonus). I've added the Bin folder to the PATH environment variable. To avoid adding the Administrator's Bin folder to the path for all users it's first necessary to locate the appropriate registry key for the Administrator's environment variables. On my server it looks something like HKEY_USERS\S-1-5-21-1234567890-1234567890-1234567890-500\Environment. Then it's just a case of adding a new Expandable String Value called PATH and giving it a value of %PATH%;%UserProfile%\Bin. The command prompt needs to be restarted in order for the change to take effect.


To cap off a fairly unproductive day, I've just been reminded that I shouldn't be using the Administrator account for administration. Absolutely correct and in fact the Administrator account should be renamed and disabled. Way back in Installing Windows Server 2008 Core part 2 I created another administrative account. Time to start using it. This means I need to move my Bin directory and apply the registry changes to a different user profile. Finally:



C:\Users\dave>iis
PS IIS:\>

Sunday, February 15, 2009

PowerShell Adventures

So I want to be able to administer IIS 7 remotely using PowerShell. Installation on W2K8r2 is easy via start /w ocsetup MicrosoftWindowsPowerShell. And there is a PowerShell Snap-in for IIS 7.0. And in case you're wondering, the location of powershell.exe is added to the path during installation, you just need to restart the console for it to take effect (i.e. there is no need to try to hack the registry in order to add the location of powershell.exe to the PATH environment variable). So a few common commands to get started:



Help

Get help for a particular cmdlet, e.g. help get-process.

Get-Alias

Returns a list of aliases for PowerShell cmdlets.

Get-Command

Returns a list of PowerShell cmdlets.

Get-Process

Returns a list of all the running processes

Get-Service | sort status,name

Get-Service | Where-object{$_.status -like "running"}

Get a list of services - in the first case sort by status and name, in the second case filter output to include only running services


Now it would be kind of cool to be able to use the new Windows PowerShell Integrated Scripting Environment to administer the server. Turns out in some cases it just works. Remoting however requires the Windows PowerShell V2 Community Technology Preview 3 (CTP3) on your Vista box. Check out the help text related to remoting:



PS C:\> help about_remote_requirements
TOPIC
about_remote_requirements

...

NOTE: Many cmdlets, including the Get-Service, Get-Process, Get-WMIObject,
Get-Eventlog and Get-Event cmdlets get objects from remote computers,
but they use .NET methods to retrieve the objects. They do not use the
Windows PowerShell remoting infrastructure. The requirements in this
document do not apply to these cmdlets.

Currently the WinRM service is stopped on the server. Looks like I need to do some configuration:



CONFIGURE WS-MANAGEMENT

The remoting features of Windows PowerShell are supported by the WinRM service, which is
the Microsoft implementation of the WS-Management protocol. To use the remoting features,
you need to change the default configuration of WS-Management on the system.

Windows PowerShell provides a script to configure WS-Management. The script is located
in the Windows PowerShell installation directory ($pshome).

To run the configuration script:

1. Open Windows PowerShell with the "Run as Administrator" option.

2. At the command prompt, type:

& $pshome\configure-wsman.ps1

Sadly there is no $pshome\configure-wsman.ps1 on either my Vista box or my server. What to do? I tracked down Installation and Configuration for Windows Remote Management. Following the instructions and running PS C:\> winrm quickconfig on the server gives me this:



PS C:\> winrm quickconfig
WinRM already is set up to receive requests on this machine.
WinRM is not set up to allow remote access to this machine for management.
The following changes must be made:

Create a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.
Enable the WinRM firewall exception.
Configure LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

Make these changes [y/n]? y

WinRM has been updated for remote management.

Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.
WinRM firewall exception enabled.
Configured LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

Cool, that all looks good. If only the same thing worked on Vista:



PS C:\> winrm quickconfig
WinRM is not set up to allow remote access to this machine for management.
The following changes must be made:

Set the WinRM service type to delayed auto start.
Start the WinRM service.
Create a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.
Enable the WinRM firewall exception.

Oh dear. I did however manage to locate PowerShell Remoting on Windows 2008 R2 Server Core. This indicates there is a built-in function called Enable-PSRemoting. Running this in my Vista PowerShell reveals:



PS C: Enable-PSRemoting
Windows PowerShell remoting features are not enabled or not supported on this machine.
This may be because you do not have the correct version of WS-Management installed or this version of Windows does not support remoting currently.
For more information, type 'get-help about_remote_requirements'.
At line:13 char:37

But I know where to get the WinRM CTP. This is a Windows Update and requires a restart. Now things are looking up:



PS C:\Windows\System32> enable-psremoting
WinRM has been updated to receive requests.
WinRM service type changed successfully.
WinRM service started.
Configured LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

WinRM has been updated for remote management.
Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.
WinRM firewall exception enabled.

But I'm not quite there yet:



PS C:\Windows\System32> Enter-PSSession -computerName myServer
Enter-PSSession : Connecting to remote server failed with the following error message : The WinRM client cannot process the reque
st. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS tran
sport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure
TrustedHosts. You can get more information about that by running the following command: winrm help config.
At line:1 char:16
+ Enter-PSSession <<<< -computerName myServer
+ CategoryInfo : InvalidArgument: (myServer:String) [Enter-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : CreateRemoteRunspaceFailed,Microsoft.PowerShell.Commands.EnterPSSessionCommand

Checking my server listener configuration I have:



PS C:\Users\Administrator> winrm enumerate winrm/config/listener
Listener
Address = *
Transport = HTTP
Port = 80
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint
ListeningOn = 127.0.0.1, 10.20.1.251, ::1, fe80::100:7f:fffe%6, fe80::5efe:10.20.1.251%4, fe
80::585e:792b:91b3:9876%3

So no joy. I need to remove the HTTP Listener and add an HTTPS Listener. I'm going to save that exercise for another post - this one's starting to go feral. Right now I'm more interested in getting the IIS PowerShell snap-in installed so I can start managing IIS. If you've read this far only to discover there is no resolution I apologise and I hope you appreciate that I share your frustration.

Tuesday, February 10, 2009

Installing Windows Server 2008 Core part 3

IIS 7.0. Time to have a look at the various options. A suggested commandline looks like the following. This is derived from Installing IIS 7.0 on Windows Server 2008 and Windows Server 2008 Packages.


C:\> start /w pkgmgr /iu:IIS-WebServerRole;IIS-WebServer;IIS-CommonHttpFeatures;IIS-StaticContent;IIS-DefaultDocument;IIS-HttpErrors;IIS-HttpRedirect;IIS-ASPNET;IIS-NetFxExtensibility;IIS-ISAPIExtensions;IIS-ISAPIFilter;IIS-HealthAndDiagnostics;IIS-HttpLogging;IIS-LoggingLibraries;IIS-RequestMonitor;IIS-HttpTracing;IIS-CustomLogging;IIS-ODBCLogging;IIS-Security;IIS-BasicAuthentication;IIS-WindowsAuthentication;IIS-DigestAuthentication;IIS-URLAuthorization;IIS-RequestFiltering;IIS-IPSecurity;IIS-Performance;IIS-HttpCompressionStatic;IIS-HttpCompressionDynamic;IIS-WebServerManagementTools;IIS-ManagementScriptingTools;IIS-ManagementService

Wading through each of the options should give some indication of what I need to install. Be warned that a sinlge misplaced character will cause the install to fail and tracking that sucker down can be a right pain. When the install has completed the last error can be checked to ensure the install was successful. An error code of 0 indicates success, an error code of -2146498548 indicates you might have accidentally typed an s on the end of IIS-ManagementService which annoyingly happens to be the last item in the command:


C:\> echo %errorlevel%

IIS-WebServerRole

The Web server that provides the Web application infrastructure for all versions of the Windows operating system.


IIS-WebServer

Installs the IIS Web server, which enables support for HTML Web sites, and the optional support for ASP.NET, classic ASP, and Web server extensions.


IIS-CommonHttpFeatures

Installs support for static Web server content, such as for HTML and image files, for custom errors, and for redirections.


IIS-StaticContent

Serves the HTM, the HTML, and the image files from a Web site.


IIS-DefaultDocument

Enables the specification of a default file, which is loaded if a user does not specify a file in a request URL.


IIS-HttpErrors

Installs the HTTP error files and enables the customization of the error messages.


IIS-HttpRedirect

Supports client request redirections.


IIS-ASPNET

Enables support for ASP.NET applications.


IIS-NetFxExtensibility

Enables a Web server to support the .NET Framework-managed module extensions.


IIS-ISAPIExtensions

ISAPI Extensions


IIS-ISAPIFilter

Enables ISAPI filters to modify the behavior of a Web server.


IIS-HealthAndDiagnostics

Monitors and manages Web server, Web site, and Web application health.


IIS-HttpLogging

Enables the logging of Web site activity for a specified Web server.


IIS-LoggingLibraries

Installs the IIS logging tools and related scripts.


IIS-RequestMonitor

Monitors the health of Web servers, Web sites, and Web applications.


IIS-HttpTracing

Enables tracing support for ASP.NET applications and failed requests.


IIS-CustomLogging

Supports custom logging for Web servers, Web sites, and Web applications.


IIS-ODBCLogging

Supports logging to an ODBC-compliant database.


IIS-Security

Supports additional security protocols for Web servers, Web sites, Web applications, virtual directories, and files.


IIS-BasicAuthentication

Simple authentication that requires a valid Windows user name and password.


IIS-WindowsAuthentication

Authentication method that uses either NTLM or Kerberos to validate a user.


IIS-DigestAuthentication

Authentication method that sends a password hash value to the Windows domain controller.


IIS-URLAuthorization

Enables a client computer to access the URLs that exist within a Web application.


IIS-RequestFiltering

Configures the rules that block selected client requests.


IIS-IPSecurity

Enables or denies content access, based on an IP address or domain name.


IIS-Performance

Enables the compression of content before the information is returned to a client computer.


IIS-HttpCompressionStatic

Compresses static content before returning the information to a client computer.


IIS-HttpCompressionDynamic

Compresses dynamic content before returning the information to a client computer.


IIS-ManagementScriptingTools

Enables the management of a local Web server by using the IIS configuration scripts.


IIS-ManagementService

Enables a Web server to be managed remotely from another computer, by using the Web Server Management Console.


Installing IIS adds the appropriate firewall rule so the proof in the pudding can be determined by opening my preferred browser and pointing it at the new server. Hooray, it's the IIS7 welcome page.

Monday, February 9, 2009

Installing Windows Server 2008 Core part 2

So not exactly square one but time to repeat a couple of steps including configuring an IP address, changing the computer name, and enabling Remote Desktop. And why does the command prompt disable QuickEdit Mode by default — would anyone complain if Microsoft took it upon themselves to set some reasonable default settings?



Having installed R2, getting PowerShell is easy:

C:\> start /w ocsetup MicrosoftWindowsPowerShell

And starting it:

C:\> windows\system32\windowspowershell\v1.0\powershell.exe

The TechNet blog indicates .Net 2.0 is an optional install on Core. It's required for PowerShell and it is installed along with PowerShell as a prereq if it isn't already installed. And while I'm doing the .Net thing I might as well install 3.0 and 3.5 (note the command prompt, not PowerShell):

C:\> start /w ocsetup NetFx3-ServerCore

Set the date/time/timezone:

C:\> control timedate.cpl

And the international settings:

C:\> control intl.cpl

Allow the Event Viewer MMC snap-in to connect through the Windows Firewall. As an aside, I originally copied the following command from a TechNet webpage and when I ran it I was getting an error Group cannot be specified with other identification conditions. It turns out the quotes used on the webpage were the issue; replacing them with the standard double quotes fixed the problem:

C:\> Netsh advfirewall firewall set rule group="Remote Event Log Management" new enable=yes

And the same for Services, Windows Firewall with Advanced Security. I would kind of like to know how to restrict the MMC connectivity to particular machines on the local network, something to add to my TODO list. Also note the group name Remote Service Management has Service singular. The sites I've been referencing, including TechNet, all have this as Services plural:

C:\> Netsh advfirewall firewall set rule group="Remote Service Management" new enable=yes

C:\> Netsh advfirewall firewall set rule group="Windows Firewall Remote Management" new enable=yes

After some digging I believe I can now at least restrict MMC connectivity to my local subnet. This obviously isn't a panacaea but does adhere to the principle of defence in depth. Note these commands can't be applied to a rules group but must instead be applied to each individual rule in the group:

C:\> Netsh advfirewall firewall set rule name="Remote Event Log Management (NP-In)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Remote Event Log Management (RPC)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Remote Event Log Management (RPC-EPMAP)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Remote Service Management (NP-In)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Remote Service Management (RPC)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Remote Service Management (RPC-EPMAP)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Windows Firewall Remote Management (RPC)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Windows Firewall Remote Management (RPC-EPMAP)" new remoteip=localsubnet

C:\> Netsh advfirewall firewall set rule name="Remote Desktop (TCP-In)" new remoteip=localsubnet

Managing services remotely from my Vista VM using the Services snap-in doesn't provide an option to specify alternate credentials and as a consequence the connection fails with access denied. The convenient workaround for this is to add an administrative account with the same username and password as my Vista account to Win2K8:

C:\> net user dave letmein /add

C:\> net localgroup Administrators /add dave


So now I have convenient access to view the event log on the server and a familiar interface for managing services. So I might as well take the opportunity to disable DHCP, DNS Client, TCP/IP NetBIOS Helper.

Monday, February 2, 2009

Installing Windows Server 2008 Core on Mac Mini

As a Mac-using Windows developer I have had for good measure a small and extremely low traffic OpenBSD web server running on an Apple Cube. It's been a fun project. The Cube happily runs headless on my shelf and lacking a fan is extremely quiet. I was intending to replace it with an Intel Mac Mini which is slightly louder but also a nice form factor and has a bit more grunt. Now I find myself needing to upskill my knowledge of Windows server products. I have in the past been responsible for maintaining and securing Internet-facing Windows 2003 servers. Time to transition to Windows Server 2008 Core Web.


An iso for W2K8 Web Server is available for download here. Leopard's Disk Utility can be used to burn the iso to a DVD. Microsoft allows the use of the software for trial purposes for a period of 60 days and provides instructions to extend that for a further 180 days. The attraction of the Web edition of W2K8 is the lowish price compared to their other server products. The limitation is the inability to run any Microsoft server products except IIS. For my purposes the limitation is not an issue.


So armed with the DVD, the next step is to partition the Mini using Boot Camp Assistant and boot from the install DVD. The install process is short and sweet, and fairly quickly the Windows login prompt is displayed. Unfortunately there's no indication what the initial username and password might be. Without referring to the documentation my first guess was that the initially entered values for username and password would be used as the identity for the administrator. No joy, no joy, fail. My second guess was correct, a username of Administrator and a blank password were accepted and a change password form displayed.


Having successfully signed on, the only UI provided with Core is a command prompt. Kind of daunting. First things first (apart from configuring the command prompt for useful defaults), how do I change the default computer name, configure an IP address, and access the Mini from my MBP using Microsoft© Remote Desktop Connection Client for Mac.



Change the name of the computer:

C:\> netdom renamecomputer {current name} /NewName:{new name} (minus the curly braces).

Note a reboot is required before the new computer name will take effect:

C:\> shutdown /r /t 0

Setting the network configuration:

C:\>netsh interface ipv4 set address name="Local Area Connection" source=static address=10.20.1.250 mask=255.255.255.0 gateway=10.20.1.1

Allow access via RDC:

C:\> Cscript %windir%\system32\SCRegEdit.wsf /ar 0


So far so good. Unfortunately it's not as simple as unplugging the display, keyboard, and mouse from the Mini. The Mini requires a video signal in order to boot successfully. A simple hack using a resistor is required, details are available from Blackfriars.


OK, at this point I've just discovered that ASP.Net is not supported on Server Core. That seems like a fairly major oversight and a little googling reveals it will be supported in Windows Server 2008 R2 which is currently in beta. So time to go back and download the beta, which succeeded finally after 4 failed attempts I can only ascribe to gremlins.


Hurdles coming thick and fast now. There's no eject button on a Mac Mini; how to extract the disk currently resident in the drive? Sadly there is no obvious mechanism to do this from a command prompt and there are many vb scripts that instantiate a media player and issue it a command to eject the drive — not feasible on Windows 2008 Server Core. Fortunately there is a simple Mac mechanism that only requires the Mini to be restarted while holding down the left mouse button. Not exactly intuitive but achieves the desired result. Next, attempting to boot the Mini from the new disk displayed a prompt "Select CD-ROM Boot Type :" and no matter how hard I press either "1" or "2" the installation progresses no further. Without going into detail there is a solution. The first step is to download oscdimg.exe. This .exe is also available in the Windows Automated Installation Kit which is a 992.2 MB download. Next and using instructions borrowed from Sergio Mcfly, assuming your Parallels Vista install maps D:\ to the DVD drive in your MBP, run the following command oscdimg.exe -n -m -bd:\boot\etfsboot.com d:\ c:\w2k8r2Web.iso. Burn the resulting iso to a new DVD and the Mini now boots from the install disk.


This time, with a clean install, I don't have to guess the Administrator username. Instead I'm immediately prompted to set the password for Administrator and I now have a command prompt and the desktop background informs me I'm running Windows Web Server 7 For testing purposes only. Build 7000.

Thursday, January 22, 2009

Upgrading to jQuery 1.3

Suddenly it's 2009 and jQuery 1.3 has just been released. Upgrading was largely a no-brainer but there were a couple of hurdles. First if using jQuery UI it is necessary to upgrade to version 1.6r5, i.e. a beta release. Hopefully a full release will be available shortly.


Having upgraded to jQuery UI 1.6r5, if you are using the tabs widget there is a bug in the code that is triggered when adding a new tab (ticket #3875). This has just been fixed in trunk. Basically when creating a tab the correct classes weren't being added and the contents of the newly created tab would be displayed on the page. Getting the latest ui.tabs.js from the subversion repository fixes the problem.



Up until now I've been using the jquery.delegate plugin to handle event delegation. I've now switched to using jQuery 1.3 live events. This required some minor surgery on the code. Where previously I had something like:



$("#someDiv).delegate('click change', ".hookAddReq", addRequirement);

I now have:



$(".hookAddReq", "#someDiv").live("click", addRequirememnt);

This bears a little more scrutiny. The class .hookAddReq is assigned to a <select/> element. I have been adding the hook prefix to class names that I use specifically for adding behaviour and I try to add behaviour using classes rather than attaching handlers to HTML elements. There are a couple of reasons for doing this. Firstly by having the hook prefix I know immediately that there is behaviour attached to an element (to distinguish the class name from classes used purely for adding styles). Secondly, using a hook class I can hopefully modify the structure of the HTML without breaking the behaviour.


In the original code I was delegating both the click and change events. This came about because I was developing using Firefox and discovered during testing with IE that the change event doesn't bubble in IE. I guess this is probably documented somewhere but I was scratching my head for a while. Having moved to jQuery 1.3 and actually reading the documentation, live events don't support the change event regardless. I still wanted to be able to delegate those events and I was able to reuse the code I had already written to handle the click events for IE (see below). Effectively just checking to see if the selectedIndex is non-zero and setting it to zero to prevent more than one click event being processed.



addRequirement = function()
{
   var selIdx = this.selectedIndex;
   if (selIdx)
   {
      this.selectedIndex = 0;
      var control = this.options[ selIdx];
      $(view).trigger("addRequirementEvent", $this.control);
   }
};


The other plugin that broke is the excellent jquery.transform that I use to convert XML from the server into HTML on the client. The version I was using still had the @ character in jQuery attribute selectors. This has been fixed in version 3.0.1 released on 20 January.



Finally, the jQuery UI skin needs to be updated. Get the latest from jQuery ThemeRoller.

Wednesday, November 5, 2008

Prototyping AJAX with Jaxer and jQuery

So where did October go? Amazon kindly delivered a bunch of great books. So far I'm part way through C# in Depth and jQuery in Action, both Manning books and good reading. As a developer it can be difficult to find books pitched at the right level and at least with these two books Manning got pretty close to the sweet spot.


Fortunately the biggest impact on my tech time apart from family is the three months work I've begun for a local SaaS outfit, TenderLink.com (which is involved in tendering, not online dating). The work I'm doing is confidential but I wanted to talk about the tools I'm using for prototyping: Aptana Studio, Jaxer, and jQuery.


So who isn't using jQuery, hell even Microsoft is getting in on the fun. If you're using jQuery you may have heard of Aptana. From their website:


Aptana is about cutting-edge infrastructure for modern, Ajax-based web applications. We already have the leading Ajax IDE in the market (roughly 2 million downloads).

Aptana Studio is based on Eclipse and you can either download the full application or if you're already using Eclipse, just download the plugin. Aptana Studio has great support for writing JavaScript code. If you've ever had to write JavaScript using the likes of older versions of Visual Studio you'll know that JavaScript, despite being the dynamic part of DHTML, has been a second class citizen for a long time. Aptana Studio really gives you the tools you would expect in order to be productive writing JavaScript, including support for all the major JavaScript frameworks. Aptana Studio is free open source software.


Aptana also integrate their Jaxer server product into Aptana Studio which has some really great possibilities. Jaxer is referred to as the world's first AJAX server. It's not necessarily a misnomer but the buzzword compliance doesn't begin to describe what a cool product it is. It uses the Mozilla engine so you get the latest versions of Mozilla JavaScript. And by tagging your script element with the runat="server" attribute your familiar JavaScript code gets executed at the server. For the work I'm doing I'm leaning heavily on jQuery to handle the UX. The server-side will ultimately be provided by the Jade enterprise object database. But for prototyping my jQuery client user experience, the Aptana Studio/Jaxer combination is exceptional.