Web/Tech

March 12, 2008

Thoughts on PHP Frameworks - Part 1 - CakePHP

Over the past year I have done quite a bit of work using the .NET Framework on various projects, so when the opportunity came up to try a project in PHP I thought it would be a nice change of pace. The project involved modifying an existing open source project for internal use. The project was built on a XAMP stack using a custom built PHP framework. After a few months of craziness the project was completed but at the end of it all I was left wondering if there was a better way to complete a PHP project. The custom PHP framework was a mess. It had huge switch statements that served as controllers, a templating engine that was confusing at best and models that were built on a wonky SQL engine. With that I set off and discovered that there were various PHP frameworks available that would, in theory, be used as a base to any PHP application. I thought it would be a good idea to try out a few of these frameworks and share my impressions of them. Hopefully, if I ever have to write another PHP application again, I would at least know where to start.

CakePHP

During my research no one PHP Framework came out as the best framework. That said I found the open source CakePHP framework to be a stand-out. It includes built-in Model-View-Controller (MVC) functionality, PHP 4 and 5 support, validation and authentication among other supported features.

The Blog Tutorial

After going through the website, gathering preliminary data and completing a general overview of CakePHP (side bar - note that a few of their screencasts were not working or did not have any sound) I decided to get down to business and try actually building an application myself. To start off I planned on following the CakePHP Blog Tutorial and then modify the code to see how easy it would be to add functionality.

I already had Apache, MySQL5 and PHP5 installed & setup on my computer from previous projects so all the was required for the CakePHP installation was unzipping the code base and changing the DocumentRoot setting in Apache (in the \Apache Group\Apache2\confhttpd.conf file) to point at the Cake install. Easy enough. I was able to see the CakePHP default page at this point and the database configuration was just as straightforward. By now I realized that the default homepage was not properly styled in CSS and that I had to install the Apache mod_rewrite module. The process is outlined in the tutorial but it required a bit of tinkering on my end (I did not need to include the "AddModule" line for instance) and an Apache restart before the changes took affect.

Now to start the actual coding... I opened the install as a PHP Project in Eclipse 3.1 using the PHPeclipse plug-in. I got through the tutorial quickly and had some basic CRUD pages going.

A few things I noticed during the process:

  • The model classes are very bare. For instance there are no properties that can be accessed through the class. All calls to get properties from each model object are done through method calls that use the property name as a parameter. There is alot of CakePHP magic going on in the background to get this data from the database.
  • Like all PHP application code, typos will be a horror to track down. I mistyped some model properties on purpose and the application continued running like nothing was the matter.
  • You write code directly into the CakePHP app folder. This means that the CakePHP distributable also serves as your code base. Makes for easy deployment but maybe a problem if somewhere down the line you want to upgrade your CakePHP version.

Modifications

After finishing the tutorial I decided to try the following 3 modifications to the code; create a clear button when adding/editing a post object, create a custom validator, and implement basic User Authentication that would bring a visitor to either a logged in home or a generic home.

My first task seemed simple enough and I begun by looking at the app/views/posts/add.thtml page as an example. On line 19 you can see the PHP code that is used to create the submit button as:


<?php echo $html->submit('Save'); ?>

This calls the HtmlHelper class and uses a method to magically generate the HTML to be your submit button. I started by looking in the HtmlHelper class to see if there was an obvious method that would do the clear. Nothing stood out so I then turned to the CakePHP API Documentation. I took a look at the HtmlHelper class there also, and realized that the API Documentation was based off the code comments.
Fortunately a quick Google search turned up this. Afterwards, a quick search through the codebase revealed that the CakePHP version I had did not have the FormHelper::button() method. This resulted in my attempt to use the same code from the bug tracker page;

$html->input('User/clear', array ('type' => 'reset', 'value'=> 'Clear'));

but the resulting form button did not work. I suspected that it was the first parameter in the method call that was causing the issue. After all I did not have User class with a clear property. The generated HTML looked like this:

<input type="reset" id="UserClear" value="Clear" name="data[User][clear]"/>

So it appeared that the input tag's name was getting set to an array value that I didn't have. However I was not able to find a string ('', NULL, 'Post/title") that would make the clear button work. I'm sure that the functionality is available... but I could not spend any more time exploring the details to figure it out.

Next up was to try some Custom Validation on the Add/Edit Post page. The Blog Tutorial off the CakePHP site outlines the basics for creating Custom Validation so I followed that. On the Add and Edit html pages I added the following field:

<p>
        Test:
        <?php echo $html->input('Post/blogtester', array('size' => '40'))?>
        <?php echo $html->tagErrorMsg('Post/blogtester', 'Must start with Blog.') ?>
</p>

In the Post Class I already had the $validate array that was created when I did the tutorial, so I modified it to look like this:

var $validate = array(

    'title' => VALID_NOT_EMPTY,
    'body' => VALID_NOT_EMPTY,
    'blogtester' => '/^Blog++.+$/'

);

The validators work using Perl regular expressions. In the above code I check to make sure that the entry in the blogtester field starts with the string "Blog". I added the blogtester field to the database and magically the validation on the Add and Edit pages was working fine. On a side note the actual blogtester value was not actually written to the database at first. I couldn't figure it out but after a few attempts it magically started working! Overall it was a painless process.

Finally I wanted to try to implement some basic User Authentication on the system and maybe point the Users at different landing pages. I followed the tutorial found here that demos some of the authentication abilities of CakePHP. It should be noted that on the tutorial it specifically mentions that the code there should not be used as a basis for any type of security and that it only shows what is possible with CakePHP. Following the tutorial I created the User model, controller and login html page. I modified the AppController base class to include the checkSession() method outlined in the tutorial. I then added into the PostController the beforeFilter() method which is similar to an on_load event.
When I tried to hit the Post view afterwards the authentication code stepped in properly but was not able to redirect me to the User view. I got a "The requested address was not found on this server." error from the URL http://localhost:8080/users/login. I checked the obvious spots first. I made sure the Controller method, the view page and model all conformed to the CakePHP standards. I tried other things like restarting Apache and MySQL but again nothing. I gave up. If I'm going to write more of these articles, I need to move on.

Impressions and Conclusion

Overall I was surprised at the level of refinement and how far along CakePHP was in development. CakePHP was very easy to setup and is designed to be very easy to deploy other applications with. The use of convention over configuration in CakePHP aides in easy setup as well as providing some very nice MVC magic so that getting a base application going is very simple. Additionally I found the code documentation as well as the references at CakePHP numerous and helpful. On the downside I can see long term development on the CakePHP platform being a painful process. The combination of a convention-based Framework coupled with the a dynamic language such as PHP would be a nightmare to debug. For anyone prone to typos, like most developers, this will be a major problem.

In the end I found that CakePHP was much better than the custom made framework I had previously used, but I still prefer developing in the .NET or J2EE environments more. CakePHP is a good product and if you had to do a PHP project it would be a good framework to start off on. For now... It's on to other things. I have a few other PHP Frameworks that I will try to review in the future and if they can match my CakePHP experience I will be happy.

February 06, 2008

Amazon EC2 - What You May Not Have Known

Amazon’s Elastic Compute Cloud (EC2) has the goal of providing flexible computing capacity in the form of a service. This service provides the user with the ability to quickly scale to the demands of an application by booting or shutting down servers in a matter of minutes. Since all these machines run in a virtual environment, you only need to pay for the resources you use. More detailed information can be found on Amazon’s EC2 home page - http://aws.amazon.com/ec2/  Much of the documentation provided by Amazon was straightforward and easy to follow so for a full walk-through see http://docs.amazonwebservices.com/AWSEC2/2007-08-29/GettingStartedGuide/.  We will assume that the reader is familiar the basics of EC2.

This article focuses on the problematic aspects of EC2 - issues that can lead to serious problems or technicalities that if ignored, can lead to frustrating hours wasted on troubleshooting and debugging.  We've learned that the single most important thing you can do for your EC2 environment is to give it a dynamic DNS solution it can use to overcome the DHCP nature of virtual machines.  Now what can you do for yourself, you ask?  Take a look at the gotchas we encountered and save yourself from dealing with the same problems.

DHCP, Dynamic IPs and DynDNS.com

One side-effect of these virtual servers is that each time one boots up, DHCP assigns them a new IP address. In all of our experience we've never been assigned the same IP after deploying a new instance of a machine.  This is highly undesirable since our web application running on Amazon EC2 would become unreachable by it's dns name and old external IP if we ever had to re-deploy a server instance after some failure.  It became evident that if a server were to go down, we'd be dealing with a significant amount of down-time.  In order to ensure that we wouldn't have to deal with a time-consuming process of modifying the configuration of servers and waiting for updates to our domain provider's DNS to propagate (up to 96 hours), we implemented a dynamic DNS solution.

Our application had the additional complexity of requiring inter-server communication. But with a new internal IP address on every re-deployment, the new locations of the server would be unknown to the others. Once again we decided that we needed these servers to have stable aliases in order to avoid reconfiguring each machine whenever we had to re-deploy a server.

In order to ensure a quick recovery after one of our servers went down, we needed to be able to update DNS entries and have the changes take effect immediately. Amazon suggests using dynamic DNS solutions such as DynDNS and ZoneEdit.  We decided to go with DynDNS because it appeared to offer better support and documentation for the service itself, as well as better instructions on how to set up  recommended update clients such as ddclient.  The ddclient tool is responsible for monitoring a machine’s IP address and updating DNS entries when a change is detected. Here is what we did to implement the dynamic DNS service for EC2:

  1. Go to https://www.dyndns.com/services/ and sign up for a free ‘Dynamic DNS’ account or a paid ‘Custom DNS’ account if you want to stick with an existing domain.
  2. Create place holder records for entries that you expect to be updated dynamically by ddclient (you can start with a bogus value like 10.10.10.10 to make it obvious when it changes).
  3. Under your preferences you can also pre-activate your solution to speed things up if you plan on delegating the name service over to DynDNS soon.
  4. Go to https://www.dyndns.com/support/clients/unix.html to download ddclient and follow the instructions in the Knowledge Base article to get the client installed.
  5. Paste the following and update your login, password and 'custom' server list in your ddclient.conf file:

    use=cmd, cmd='curl http://169.254.169.254/2007-08-29//meta-data/public-ipv4'
    login=xxxxx
    password=xxxxx
    protocol=dyndns2
    server=members.dyndns.org
    wildcard=YES
    custom=yes, your.server1.com, your.server2.com

  6. Note that the client requires the perl-IO-Socket-SSL module to be installed so using yum, the following command should do the trick - "yum install perl-IO-Socket-SSL.noarch"
  7. You can also choose to make sure the ddclient daemon is running when the machine boots up by using chkconfig with the service or start  it with "service ddclient start"

Note that the url in the configuration above is Amazon's recommended way to obtain a server's external IP address and http://169.254.169.254/2007-08-29//meta-data/local-ipv4 will give you an machine's internal IP.  That’s it. After getting the client set up you can log into your DynDNS account to see that your records are being updated and now you can access your servers using those DNS names without worrying about unexpected changes to the IP address of your servers.

Here are some things to consider when using ddclient.  ddclient maintains a cache by default in “/var/cache/ddclient/” which can prevent any updates to DynDNS if a record is updated outside of the client - remember to delete the cache in this situation.  If you want to keep both a machine's external and internal dns names up to date, you would need to run multiple instances of the ddclient daemon. Note however, that you must modify both the startup script (provided by them) to handle multiple instances as well as the .pid values to be unique in each of the .conf files. This may be too much work so a simple alternative is to have separate cron-jobs that call ddclient with the ‘–force’ flag along with the location of each ddclient.conf file with the ‘–file’ parameter.

Other ‘Gotchas’ and Issues We Ran Into

This section covers unexpected issues that we ran into our first time around working with EC2. These issues are centered around 3 areas - choices in machine images, packaging your images and disk usage on the virtual servers.  It’s helpful to be aware of these issues because you may otherwise end up wasting time troubleshooting the same problems we did.

Choices in Machine Images

Remember that you don't have to use Amazon’s base Fedora Core 4 images to build your machines. Before spending too much time configuring and customizing an AMI, find one that suits your needs from the start so you won't have to redo any work later on down the road. Check out the list of public AMIs in Amazon’s resource center for something that is more suitable for your needs: http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=101 We started with the standard Fedora Core 4 build but eventually moved to a more up-to-date enterprise CentOS build provided by rightscale.com for better maintainability and security.

Packaging Your Image

When packaging up your own image using the ‘ec2-bundle-vol’ command, make sure you specify a clean folder using the '–d' flag otherwise bundling the same image twice will result in an error due to the conflicting sets of temporary files. Also, use the '–p' flag to specify a prefix/name for your image otherwise when you upload the AMI and look at your list of images with the "ec2-describe-images –o self" command it will be very hard to differentiate between all the images that you’ve created.  For example, we used something like "ec2-bundle-vol -k pk-XXXXXXXXXXXXXX.pem -u 123456789 -c cert-XXXXXXXXXXXXX.pem -d /mnt/cleantempfolder -p web-server-v1".

Machine Disk Usage

When working with your image note that the main drive/partition (where the system files are) has a very limited capacity (10 GB in our case). So when dealing with large files/directories use ‘/mnt’ as it has over 100 GB.  We've experienced all sorts of failures after accidentally maxing out the main partition.  Remember that if you are running an application that generates log files or temporary/residual files on disk, you will need to make sure you don't cause failures by filling up the main partition with large files and directories.

If a machine is terminated, all your data will be lost except for what was backed up from the last time you ran an 'ec2-bundle-vol'. Be mindful of where you put your files because when bundling your machine image, many directories are excluded by default so it's easy to lose data.   Check the sample output of the ‘ec2-bundle-vol’ command to see which directories don't get backed up - http://docs.amazonwebservices.com/AWSEC2/2007-08-29/GettingStartedGuide/creating-an-image.html

Good Luck!

With a dynamic DNS solution, Amazon EC2 servers face significantly less down-time if something goes wrong and are much easier to maintain in the long run. Amazon has provided a set of very useful tools that make it simple to build, upload and deploy customized machine images. Overall, I have to say that Amazon’s EC2 platform has been relatively easy to work with and is worth considering as a hosting solution.

January 31, 2008

Your First Facebook Application

Writing a Facebook Application

With the rise of social network applications such as Facebook over the last 3 - 5 years, it's become very attractive to be present in that space.  Facebook applications are most often games that allow users to play against each other, or themselves in the case of solo-games, but there is no technology limit to this. The application could easily be an extension of your own application.

One of the many great things you get from using the Facebook platform, is an enormous list of potential clients who already have user/login info for your application! Couple this with the outsourcing of user login/management/authentication to another provider (Facebook), access to the Facebook messaging API, and it's easy to see the return on investment.

Getting Started

Assuming you have a Facebook account , you will need to install the Facebook developer application . Once you have the developer application installed, you should be directed to the developer home page, and here you can apply for an application key. When you apply for an application key, it asks for an application name, we'll call ours Hello World. Now that you have a Facebook application registered, you can retrieve your API key, and secret key from the installed applications page.

We will be building a .NET application using the .NET Facebook dev kit from Microsoft , but you can use any modern language. There are development libraries available for all of them, and the developers wiki is a great resource... here is the link.

Assuming you will be developing using your local machine, we need to configure a couple values in your Facebook application, which can be done on the application settings you can access from the applications page here

  • Set your Callback URL to your local instance url (e.g. http://localhost/); if you are using Visual Studio's built in server/debugging, then you will need to fix the port that it uses, and enter that as your Callback URL. If you are using a public server, then enter the URL for that server in this box.
  • Set your Canvas Page URL to anything you like, this is the url that Facebook users will use to access your application.
  • Set the application to iFrame - This allows you to have any content you choose, without being limited by the FBML (Facebook Markup Language) and it's slow rendering times.
  • Set the application type to Website.

There are many more options there that you can play with , but these are the ones you need to get your application running.

After installing the Facebook Dev Kit from Microsoft, you will have the Facebook libraries located in the install location (C:\Program Files\Facebook Developer Toolkit). There will now be Facebook components available in Visual Studio, but we will do our example by hand.

The Code

Create a new web page project, and open Default.aspx to insert the following content:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="HelloWorld.aspx.cs" Inherits="HelloWorld._default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>FaceBook Hello World</title>
</head>
<body>
    <form id="form1" runat="server">
        <h1>Ours</h1>
    <asp:Repeater ID="friendList" runat="server">
    <HeaderTemplate>
    <table>
    <tr>
    <td><b>Name</b></td><td><b>Photo</b></td>
    </tr>
    </HeaderTemplate>
    <ItemTemplate><tr><td><%#DataBinder.Eval(Container.DataItem,"Name")%></td>
    <td><img src="<%#DataBinder.Eval(Container.DataItem,"PictureUrl")%>" /></td></tr></ItemTemplate>
    <FooterTemplate>
    </table>
    </FooterTemplate>
    </asp:Repeater>
    </form>
</body>
</html>

Now open the code behind file Default.aspx.cs , and populate it with this

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.ObjectModel;
namespace
HelloWorld
{
    public partial class _default : System.Web.UI.Page
    {
        private Facebook.Components.FacebookService _facebookApi = new Facebook.Components.FacebookService();

        protected void Page_Load(object sender, EventArgs e)
        {

            // ApplicationKey and Secret are acquired when you sign up for
            _facebookApi.ApplicationKey = "[YOUR KEY]";
            _facebookApi.Secret = "[YOUR SECRET KEY]";
            _facebookApi.IsDesktopApplication = false;

            string sessionKey = Session["Facebook_session_key"] as String;
            string userId = Session["Facebook_userId"] as String;

            // When the user uses the Facebook login page, the redirect back here will will have the auth_token in the query params

            string authToken = Request.QueryString["auth_token"];

            if (!String.IsNullOrEmpty(sessionKey))
            {
                _facebookApi.SessionKey = sessionKey;
            }
            else if (!String.IsNullOrEmpty(authToken))
            {
                _facebookApi.CreateSession(authToken);
                Session["Facebook_session_key"] = _facebookApi.SessionKey;
            }
            else
            {
                Response.Redirect(@"http://www.Facebook.com/login.php?api_key=" + _facebookApi.ApplicationKey + @"&v=1.0");
            }

            if (!IsPostBack)
            {
                // Get our list of friends
                Collection<Facebook.Entity.User> friends = _facebookApi.GetFriends();

                //bind our repeater
                friendList.DataSource = friends;
                friendList.DataBind();
            }
        }
    }
}

The basics here should be clear. You set your application and secret keys, and then your basically good to go! The Facebook.Components.FacebookService class provides access to the current user, and if they aren't logged in you can bounce them to the Facebook login screen.

To go live with your project, simply move the code to a public server, change your callback url in the application settings page within Facebook and select the 'add application to directory' check box on that same page.

Now that you have the user authenticated, and access to their friends list, the sky is the limit. We chose to store user keys in our database for customizations which we simply did using their Facebook ID.

In Conclusion

In practice I found the Facebook API to be very usable, and there were no unexpected hurdles to work around. We did have an occasional user authentication error, which may have been caused by having mutliple users logged in on the same machine, but this was an edge case.

In all, I wouldn't hesitate to recommend the platform as an extension of an existing business giving a low-cost, low-risk entry into the seemingly persistent world of Social Networking Applications. However, I would hesitate before putting serious development into a FBML application, or anything that specifically required Facebook, for the same reasons I wouldn't want to own a million dollar application that only ran in Friendster, Tribe.net or any of the other Social Networking sites that had their time in the sun.

January 13, 2008

The Rise of Utility Computing?

Many years ago, companies like IBM and Sun promised us that the age of utility computing was here, and that we would soon be purchasing computing time in the same manner as we currently purchase electricity, gas, telephone and water. However, people laughed at the idea, and it faded into the backs of peoples' minds.

Codesta recently helped to create a brand new startup company called Jamloop (found at http://www.jamloop.com) which aggregates and geolocates new and used musical instruments. JamLoop came to Codesta and asked us to help make their idea reality. Based upon their requirements we decided to go with a JBoss-based web application with Spring and Hibernate underpinnings, which is (I believe) a pretty common choice and is not revolutionary. When deciding on how to bring this site to production however, we tried something different. We researched Amazon's EC2 (Elastic Compute Cloud), and we found it to be a good fit for both us and for our client. For 10 cents an hour ($2.40 a day) we could have a server available to us to do anything we wanted. We elected to go this route, and it has paid off handsomely for both us and the customer.

From our perspective, the key advantages that EC2 provide are:

  • JamLoop didn't need to purchase expensive hosting space or hire any IT people, or take a risk with a cheap hosting site - Amazon is a big enough name that we felt we could trust them
  • JamLoop can adapt to changing traffic patterns - if they suddenly get more popular or see a traffic spike, they can instantiate new EC2 instances on demand and still be paying just $0.10/hour/server
  • If JamLoop has a normal traffic load which 20 servers can handle, and a peak that 100 servers can handle, they don't need to always have 100 servers - they can scale up and down when needed
  • Since this site operates as an aggregator, it needs to import external data - JamLoop can spawn up some instances which do harvesting, keep them around for as long as needed, and shut them down when the task is complete.
  • The cost is really low - $2.40 per day of server time works out to about $72/month per server, which seems like a good price especially given that there are no contracts and it's a pay-as-you-go model
  • JamLoop can run any operating system or software that they want since these are their boxes - they aren't constrained to what a provider will set up for them e.g. Apache and PHP
  • Using the EC2 API was really easy - they have an excellent tutorial and a well-built set of tools

One of my coworkers, Oliver Chan, will be posting about what we did with EC2 from a technical perspective very soon - however, the business ramifications I found too interesting to pass by. I think that we're entering the realm of utility computing which was described and dismissed in the past, and I think it makes the barrier to entry for new companies much lower. If I decide tomorrow that I have a great idea and I want to create a new website/company around it, I don't need to think about production problems - I can just use EC2, and pay less per month for a dedicated server than I pay for electricity at home. Amazon is my ops team, and my costs are predictable and clear.

December 17, 2007

.NET Gotchas: Part I – Multi-line Text boxes and the DefaultButton in ASP.NET 2.0

Have you ever received an error message that resisted even the mighty Google? An application failure that it seems only happens to you and 10 other people in the world (all of whom, as luck would have it, speak different languages)? Have you ever felt you were going mad, staring at the same piece of code over and over again, knowing it should work - willing it to work because it should, but it does not?

Good news! You are not going mad (at least not in this!). Sometimes, the fault lies not with your code, but with your tools. I suppose this is a glancing compliment to Microsoft, because frankly it had never occurred to me that the fault could lie with their products (as I type that it feels peculiar to say, but there it is). As our lives as developers are made easier in many ways due to new and better tools, this is due to a shifting, not removal, of complexity. That complexity has moved to our tools and effects are bound to pop up.

If you were a carpenter, you would be well within your rights to show surprise at your hammer behaving like a saw. Likewise, as a developer, you are, I believe, within your rights to be surprised when your tools do not respond in the expected manner.

My intent is to produce a series of articles providing succor to fellow developers faced with those .NET issues that crop up for which information is difficult to find. By their very nature, these issues should all be short-lived affairs, with corrections (hopefully) on the way in newer releases of the .NET framework or the Visual Studio IDE. However, in the meantime, I hope these articles will help prevent frustration for somebody else.

Many of the issues we explore here are not being explored due to their ground-breaking solutions or because the problems are of such an interesting, cutting-edge nature that the world will want to tune in to watch it all as it happens. On the contrary, they will most likely be mundane, uninteresting solutions to mundane, uninteresting problems. In fact, you will likely not believe that there could ever be such problems, but they can and do crop up.

Let's get to it.

.NET Gotchas - Part I – Multi-line Text boxes and the DefaultButton in ASP.NET 2.0

Different browsers each have their own default behaviour detailing what actions to take when a user hits enter in a form. This is well and good, but there seems to be some natural, unspoken standard that says one should be able to submit a form by simply pressing enter. Through the use of JavaScript, web developers have been able to happily and easily handle this requirement for years. In designing the ASP.NET portion of the .NET Framework 2.0, Microsoft, not being oblivious to this unspoken standard, provided methods of handling and implementing this desired behaviour.

The HtmlForm class contains a property called DefaultButton which gets or sets the child control of the HtmlForm that causes postback when the ENTER key is pressed. When the DefaultButton property is not explicitly set, the browser resorts to whatever its default behaviour is. As you would expect, this feature has been implemented with JavaScript emitted when the page is generated. ASP.NET includes a special handler named WebResource.axd designed to retrieve and serve assembly resources and all of the JavaScript code that makes up the magic of ASP.NET is embedded in such resources and served to the client browser through WebResource.axd.**

Sounds effortless, does it not? A quick mental ‘Thank you Microsoft’ and our work should be completed, right?

Unfortunately, it is not (never) quite so simple. Although I was quite happy with the DefaultButton solution and it was chugging happily along for me on many forms, things all fell apart when I threw a multi-line text box into the mix.

Let's create a web form to illustrate the problem:

<form id="form1" runat="server">
     <asp:Panel runat="server" DefaultButton="btnSubmit">
          Textbox 1:
          <asp:TextBox ID="TextBox1" runat="server" TextMode="MultiLine"/>
          <asp:Button ID="btnSubmit" runat="server" OnClick="SubmitClick" Text="Submit"/>
          <asp:Label ID="Label1" runat="server" Width="500px"/>
     </asp:Panel>
</form>

Your code-behind should resemble the following:


public partial class testbuttons : System.Web.UI.Page
{
     protected void Page_Load(object sender, EventArgs e)
     {
          this.Label1.Text = "";
     }

     protected void SubmitClick(object sender, EventArgs e)
     {
          this.Label1.Text = "Submit button clicked";
     }
}

If you have developed a website for use in multiple browsers, you may have bumped your head against an interesting issue with this feature. In Internet Explorer, load up your page with a form that has the DefaultButton duly set, type in some lines of text into the text box. Go ahead and type in many lines - feel the heady rush that overcomes you as the textbox bends to your will, quietly inserting newline characters into the textbox with each and every stroke of the ENTER key! Now, load up that self-same page in Firefox, start typing in some text and hit ENTER.

What happened? Our joy has been interrupted!

What happened is that our handy-dandy multi-line text box no longer acts like a multi-line text box. Rather than causing a new line, hitting ENTER activates the default button.

The reason for this behaviour is the FireDefaultButton JavaScript code that is responsible for supplying the behaviour. Unfortunately, it seems that some IE-specific code managed to make make its way into that method, the end result being that Firefox (and other browsers using a different object model than IE) will not run the code as expected.

Here is the code for FireDefaultButton with the offending parts emphasized:

	
function WebForm_FireDefaultButton(event, target) 
{
    if (event.keyCode == 13 &&
        !(event.srcElement &&
        event.srcElement.tagName.toLowerCase() == "textarea")) {
        var defaultButton;
        if (__nonMSDOMBrowser) {
            defaultButton = document.getElementById(target);
        } else {
            defaultButton = document.all[target];
        }
        if (defaultButton && typeof defaultButton.click != "undefined") {
            defaultButton.click();
            event.cancelBubble = true;
            if (event.stopPropagation) {
                event.stopPropagation();
            }
            return false;
        }
    }
    return true;
}

The property event.srcElement is not understood by Firefox which causes our problem. That's all. As I said, dead simple, but that simplicity is cold comfort when you are shaking your fist at a monitor for 3 hours cursing the heavens because you can see no reason why your code isn't working as it should. The fix? Equally as simple. In my case, I added a new variable, var element = event.target || event.srcElement; and replaced event.srcElement with element, resulting in:


function WebForm_FireDefaultButton(event, target) 
{
    //event.srcElement doesn't work in FF so we check whether
    //it or event.target exists, using whichever is returned
    var element = event.target || event.srcElement;
    
    if (event.keyCode == 13 &&
        !(element &&
        element.tagName.toLowerCase() == "textarea"))
        {
        var defaultButton;
        if (__nonMSDOMBrowser)
        {
            defaultButton = document.getElementById(target);
        }
        else
        {
            defaultButton = document.all[target];
        }
        if (defaultButton && typeof defaultButton.click != "undefined")
        {
            defaultButton.click();
            event.cancelBubble = true;
            if (event.stopPropagation)
            {
                event.stopPropagation();
            }
            return false;
        }
    }
    return true;
}

So that is the fix and to put it all together, we just add a call to ClientScript.RegisterClientScriptInclude giving it the name and path to our newly fixed script:


protected void Page_Load(object sender, EventArgs e)
{
     /*register fixed FireDefaultButton script*/
     ClientScript.RegisterClientScriptInclude("FixFireDefault", 
           "<<PATH TO SCRIPT>>/FireDefaultButtonFix.js");
}

And that is it. When your page loads, your new script will be registered, taking the place of the original.

**(For a more detailed explanation of Web Resources, check out this Microsoft article.)

December 04, 2007

Are you in the Loop?

Get in the loop at JamLoop.com, a music oriented search engine. Looking for a particular instrument and not sure where to go or what’s out there? Looking for that particular vintage guitar, or piece for your collection? Let JamLoop bring the power of the web to you. Just type in the keywords, and let JamLoop’s search engine do the work. JamLoop indexes content from multiple classifieds sites, used equipment stores and new equipment retailers for its users to bring results to a single destination using a customized spidering technology. JamLoop shows you relevant location-based results using a mashup of IP2Location's innovative geolocation solution in conjunction with the Google Maps API.

 

To help make JamLoop’s ideas a reality we used a variety of leading-edge technologies including Amazon EC2, JBoss and Lucene. For the end user, JamLoop’s site incorporates sliders and other custom controls to enhance and enrich their user experience.

 

JamLoop’s founder came to us with an idea. He wanted a better way to find musical instruments on the web, and needed help designing the solution. Our customized Agile Project Methodology  is ideally suited for such an engagement. Through regular scrum meetings and our change-friendly approach, we delivered what you see today. We have captured new ideas along the way and continue to work on these User Stories to help JamLoop deliver a rich, personalized experience for its end user.

 

We have formed a strong partnership with JamLoop, and it has been very exciting for our team to be working with our client to help make their ideas reality. Special thanks to the team of Matt Butler, Oliver Chan, and Joe Nowak who delivered this product. Now for those reading this post, it’s time for you to get in the loop at JamLoop.com.

November 08, 2007

Installing ImageMagick with FreeType, PNG, JPEG, AI and EPS Support

Often a client will request some type of image manipulation performed server side to support their application. A common example of this is the simple 'add watermark' function that you see on so many sites, but the examples can quickly get more complicated including items such as sharpen, add text, overlay images, overlay eps drawings etc...

For more complicated image manipulation applications you need to move to a larger solution than the built-in image handlers in most languages. For just such an occasion we settled on the ImageMagick library, with associated extensions. We are communicating with ImageMagick using php and the image magick libraries, but the focus of this article is the ImageMagick setup itself. Generally, we found the environment to be tricky to configure and not very well documented.

Without going into unnecessary detail about the various challenges we faced, we'd like to provide a rough guide to setting up this environment. The most significant problem solving came around how ImageMagick finds its delegates (jpeg, jasper, libpng, ghostscript). Essentially ImageMagick expects these to be found within an 'ext' directory within the build source tree, that is not created by default.

Building the system in the /tmp directory on a clean linux system (we used a fresh install of White Box Linux, Respin 1) you should be able to follow these directions:

1) "Get" your copy of ImageMagick.
>wget ftp://ftp.imagemagick.net/pub/ImageMagick/ImageMagick-6.2.9-8.tar.gz

2) Get your Delegates.
- These are add-ons that will handle various formats/functions for ImageMagick.

For jpeg support
>wget wget ftp://Ztp.imagemagick.org/pub/ImageMagick/delegates/jpegsrc.v6b.tar.gz
For jpeg2000 support
>wget wget ftp://ftp.imagemagick.org/pub/ImageMagick/delegates/jasper-1.701.0.zip
For PNG support
>wget ftp://ftp.imagemagick.org/pub/ImageMagick/delegates/libpng-1.2.12.tar.gz
For GhostScript eps / ai support
>wget http://www.peregrinehw.com/downloads/gd/ghostscript-8.15.tar.bz2
For GhostScript fonts support
>wget http://www.peregrinehw.com/downloads/gd/ghostscript-fonts-std-8.11.tar.gz

3) Extract all of the Delegates.
- Copy the ghostscript fonts to the default dir.
>mkdir /usr/local/share/ghostscript/fonts
>cp ./ghostscript-fonts-std-8.11/* /usr/local/share/ghostscript/fonts/

4) Make an ‘ext’ directory in ImageMagick.
>mkdir ./ImageMagick-6.2.9-8/ext

5) Copy all the delegate folders to the 'ext' directory and give them nicer names such as:
jpeg
jasper
libpng
ghostscript

6) Configure and build each of your delegates.
>cd /tmp/ImageMagick-6.2.9-8/ext/jpeg
>./configure
>build

Repeat this for each of the delegates

7) Copy the jpeg folder into the ghostscript folder.
>cd /tmp/ImageMagick-6.2.9-8/ext/ghostscript
>./configure
>build
>cd /tmp/ImageMagick-6.2.9-8/ext/jasper

>./configure
>build
>cd /tmp/ImageMagick-6.2.9-8/ext/libpng

>./configure
>build

8) Create a text file for the configuration parameters for ImageMagick
>cd /tmp/ImageMagick-6.2.9-8/
>touch myconf
>vi myconf

- Here are the contents of our configuration file, have a look and make sure it matches your environment

./configure –with-windows-font-dir=/usr/share/fonts/monotype/TrueType –enable-shared –with-exif=yes –enable-lzw=yes –with-gs-font-dir=/usr/local/share/ghostscript/fonts -without-x CPPFLAGS=’-I/tmp/ImageMagick-6.2.9/ext/jpeg -I/tmp/ImageMagick-6.2.9/ext/libpng -I/tmp/ImageMagick-6.2.9/ext/jasper’ LDFLAGS=’-L/tmp/ImageMagick-6.2.9/ext/jpeg -L/tmp/ImageMagick-6.2.9/ext/libpng -L/tmp/ImageMagick-6.2.9/ext/jasper -lfreetype’

9) Run Configure
>./myconf > configure.log

- After configuration, inspect the configure.log , with any luck it will look like this. As you can see jpeg2000 failed which was not a concern to our project, but possibly if you did a make build on jasper before configuring Imagemagick it would work.

Shared libraries  –enable-shared=yes        yes
Static libraries  –enable-static=yes        yes
Module support    –with-modules=yes        yes
GNU ld            –with-gnu-ld=yes        yes
Quantum depth     –with-quantum-depth=16    16

Delegate Configuration:

BZLIB             –with-bzlib=yes        yes
DPS               –with-dps=yes        no
FlashPIX          –with-fpx=no        no
FontConfig        –with-fontconfig=no        no
FreeType          –with-freetype=yes        yes
GhostPCL          None                pcl6 (unknown)
Ghostscript       None                gs (8.15)
Ghostscript fonts –with-gs-font-dir=/usr/local/share/ghostscript/fonts    /usr/local/share/ghostscript/fonts/
Ghostscript lib   –with-gslib=yes        no
Graphviz          –with-gvc=yes        no
JBIG              –with-jbig=yes        no
JPEG v1           –with-jpeg=yes        yes
JPEG-2000         –with-jp2=yes        no
LCMS              –with-lcms=yes        no
Magick++          –with-magick-plus-plus=yes    yes
PERL              –with-perl=yes        /usr/bin/perl
PNG               –with-png=yes        yes
RSVG              –with-rsvg=no        no
TIFF              –with-tiff=yes        no
Windows fonts     –with-windows-font-dir=/usr/share/fonts/monotype/TrueType    /usr/share/fonts/monotype/TrueType/
WMF               –with-wmf=yes        no
X11               –with-x=no            no
XML               –with-xml=yes        yes
ZLIB              –with-zlib=yes        yes

10) Now run the "make" command.
>make && make install

- NOTE - This will take a long time.

11) On a succssesful build, you should be able to run this command line

>convert -background green  -fill white -font Arial  -size 165x40  label:"Codesta.Com" ImageMagickTest.gif

- Producing This Image

Imagemagicktest



For more examples of ImageMagick Useage, check here.

Related Links

White Box Linux - http://www.whiteboxlinux.org
Image Magic - http://www.imagemagick.org  
The Image Magick delegates repository - http://ftp.fifi.org/ImageMagick/delegates/
GhostScript - http://www.peregrinehw.com/downloads/gd/ghostscript-8.15.tar.bz2
GhostScript Fonts - http://www.peregrinehw.com/downloads/gd/ghostscript-fonts-std-8.11.tar.gz
Jpeg Libraries - ftp://Ztp.imagemagick.org/pub/ImageMagick/delegates/jpegsrc.v6b.tar.gz
Jasper (Jpeg2000 libraries) - ftp://ftp.imagemagick.org/pub/ImageMagick/delegates/jasper-1.701.0.zip
PNG Libraries - ftp://ftp.imagemagick.org/pub/ImageMagick/delegates/libpng-1.2.12.tar.gz

October 29, 2007

Adobe AIR bus tour, Toronto stop

I recently attended the Adobe OnAIR Bus Tour when it passed through Toronto. The tour was an event dedicated to teaching developers more about the new Adobe Integrated Runtime (AIR), which is currently still in beta. It was an interesting day of presentations, as well as forays into the realms of Halo 3 and Guitar Hero II.

The day started with a long line (doesn't everything in a big city involve long lines?), but we were eventually herded through the front doors of the Guvernment nightclub and given our loot bags. In addition to the standard bundle of marketing material, there was a t-shirt, a CD containing Adobe AIR, documentation and numerous example applications which ran on AIR (along with source code), plus a paper copy of the Adobe Integrated Runtime (AIR) for JavaScript Developers Pocket Guide. Once in the main room, a good breakfast awaited us (fresh fruit, good muffins, and assorted other things) and after we had a chance to grab our food and seats, the show began.

The first presenter, Mike Downey, greeted us and apologized in advance for being tired - Halo 3 had been released a few days before while the AIR bus was in New York, and the conference team had stayed in line until midnight to grab one of the first copies. He then gave us an overview of AIR, and discussed it's place in the Flash platform. The next presenter was Mike Chambers, and his presentation described in a bit more technical detail what AIR was, and also showed how to create a simple, Hello World application with Flex. This was followed by a similar presentation by Kevin Hoyt which dealt with creating an AIR application with HTML and Javascript, as well as going over his stopwatch example. He also did a second presentation which discussed how to get at the Flash APIs from inside of HTML via JavaScript. It was quite interesting and means that you can do Flash transitions on HTML elements. During lunch, several people took advantage of the upstairs video game room to play some WiiSports and Guitar Hero II.

After lunch, Daniel Dura gave a presentation about the AIR APIs - there seems to be quite a bit there! There's an embedded SQLite database, support for multiple operating system window types (e.g. standard and dialog box), full file system I/O with native dialogs, access to the system clipboard and drag-and-drop operations, online/offline autodetection, and a bunch more things. It was during this presentation that I finally understood what was meant when Mike Chambers said that AIR was a full-fledged application development framework. I came into this presentation thinking that AIR was primarily a presentation layer and was a competitor to Silverlight or JavaFX. It's actually more of a competitor to the .Net Framework and the Java SDK, and is meant to be a cross-platform application development framework.

The next few presentations were by Adobe partners, the first was by Rick Greenwald of SalesForce.com. Rick showed off an application which uses the SalesForce Apex API. The application had automatic online/offline handling, and would synchronize its data automatically when connectivity was restored (very slick). Then, a fellow from Akamai discussed a free video player that they've released for the community. Reading the documentation makes me think that it's designed to stream Flash videos from Akamai's Content Distribution Network (CDN), but that's not much of a surprise. Then, a fellow from Yahoo took the stage and showed us sample applications built on AIR, such as Flump (Flickr Dump), which pulls publicly available images for a given user from Flickr.com through the Flickr web services API. Finally, a fellow named Grant Skinner talked about his company and some of the things they've done with AIR and Flash in general.

After the dinner break, we wrapped up the day by discussing creating desktop applications with AIR. Lee Brimelow talked about features such as automatic application icon generation from PNG and managing drag and drop between the operating system and the AIR application. We also learned about the AIR Bus API (Yes, the bus has an API) which allows you to get assorted information such as location, Twitter feeds and pictures from the bus through web services. We also learned that AIR beta 2 would be coming out at Adobe MAX, and that AIR 1.0 is expected to be released early next year, along with Flex 3. The official conference was then adjourned, and 4-player Halo 3 was set up on the projector - a good end to a busy day.

October 01, 2007

Another opinion on the OSGi/JSR 277 debate

I recently posted an article about the debate surrounding JSR 277 and OSGi. Some quick background on the debate is:

  • OSGi (a.k.a. JSR 291) is a successful technology for packaging up Java software in a modular manner, and it has over 8 years of development behind it. It is also used in popular products like Eclipse and WebSphere
  • Sun is trying to develop a technology for adding components in a modular manner to the JVM - this is being handled as part of JSR 277, which is expected to be in Java 7
  • There are two OSGi experts on the JSR 277 expert group

At this point, the rest seems obvious - Sun will utilize the experience and learning of the OSGi experts, and create a modular system based off of OSGi.

Well, that's incorrect. Reading the JSR 277 mailing list has given people the impression that the two OSGi experts are being ignored, and that the bulk of the JSR 277 expert group appears to be making the same mistakes OSGi did at the beginning. There is also speculation that the expert group wants to do everything by itself, and doesn't want to use a technology that was developed by an external organization (referred to as "Not Invented Here syndrome"). This has caused a furious debate in the Java community around JSR 277, and there does not appear to be a resolution in sight.

The idea behind JSR 277 is a good one - it is an excellent idea to make the JVM itself modular, as this will allow it to be far more flexible in many situations. For instance, Java is used a lot on the server side - for those applications, you don't need Swing or any of the Java graphics libraries in your JDK - they can be removed. Conversely, if you develop an application and it wants to use several open source libraries e.g. Hibernate and Spring, then under JSR 277 there is a repository that the JVM would automatically look up those libraries in, and it would be able to dynamically install that capability into the JVM. That is a feature with a ton of potential (I've used Maven 2.0, it's amazing to not have to manually find libraries), and I'd hate to see it become unmanageable or broken due to political bickering. In my opinion, the JSR 277 expert group should start with JSR 291 as the basis since it is a proven, industry-accepted technology, and then explicitly change whatever is necessary to support the new requirements like ClassLoader handling and the JSR 277 repository mechanism. This is Glyn Normington's dream solution, and in a non-political, technology-centric world I believe it is the best possible solution.

-->

March 12, 2008

Thoughts on PHP Frameworks - Part 1 - CakePHP

Over the past year I have done quite a bit of work using the .NET Framework on various projects, so when the opportunity came up to try a project in PHP I thought it would be a nice change of pace. The project involved modifying an existing open source project for internal use. The project was built on a XAMP stack using a custom built PHP framework. After a few months of craziness the project was completed but at the end of it all I was left wondering if there was a better way to complete a PHP project. The custom PHP framework was a mess. It had huge switch statements that served as controllers, a templating engine that was...read more

Categories: Web/Tech

February 6, 2008

Amazon EC2 - What You May Not Have Known

Amazon’s Elastic Compute Cloud (EC2) has the goal of providing flexible computing capacity in the form of a service. This service provides the user with the ability to quickly scale to the demands of an application by booting or shutting down servers in a matter of minutes. Since all these machines run in a virtual environment, you only need to pay for the resources you use. More detailed information can be found on Amazon’s EC2 home page - http://aws.amazon.com/ec2/ Much of the documentation provided by Amazon was straightforward and easy to follow so for a full walk-through see http://docs.amazonwebservices.com/AWSEC2/2007-08-29/GettingStartedGuide/. We will assume that the reader is familiar the basics of EC2. This article focuses on the problematic aspects of EC2...read more

Categories: Web/Tech

January 31, 2008

Your First Facebook Application

Writing a Facebook Application With the rise of social network applications such as Facebook over the last 3 - 5 years, it's become very attractive to be present in that space. Facebook applications are most often games that allow users to play against each other, or themselves in the case of solo-games, but there is no technology limit to this. The application could easily be an extension of your own application. One of the many great things you get from using the Facebook platform, is an enormous list of potential clients who already have user/login info for your application! Couple this with the outsourcing of user login/management/authentication to another provider (Facebook), access to the Facebook messaging API, and it's easy...read more

Categories: Web/Tech

January 13, 2008

The Rise of Utility Computing?

Many years ago, companies like IBM and Sun promised us that the age of utility computing was here, and that we would soon be purchasing computing time in the same manner as we currently purchase electricity, gas, telephone and water. However, people laughed at the idea, and it faded into the backs of peoples' minds. Codesta recently helped to create a brand new startup company called Jamloop (found at http://www.jamloop.com) which aggregates and geolocates new and used musical instruments. JamLoop came to Codesta and asked us to help make their idea reality. Based upon their requirements we decided to go with a JBoss-based web application with Spring and Hibernate underpinnings, which is (I believe) a pretty common choice and is...read more

Categories: Web/Tech

December 17, 2007

.NET Gotchas: Part I – Multi-line Text boxes and the DefaultButton in ASP.NET 2.0

Have you ever received an error message that resisted even the mighty Google? An application failure that it seems only happens to you and 10 other people in the world (all of whom, as luck would have it, speak different languages)? Have you ever felt you were going mad, staring at the same piece of code over and over again, knowing it should work - willing it to work because it should, but it does not? Good news! You are not going mad (at least not in this!). Sometimes, the fault lies not with your code, but with your tools. I suppose this is a glancing compliment to Microsoft, because frankly it had never occurred to me that the fault...read more

Categories: Web/Tech

December 4, 2007

Are you in the Loop?

Get in the loop at JamLoop.com, a music oriented search engine. Looking for a particular instrument and not sure where to go or what’s out there? Looking for that particular vintage guitar, or piece for your collection? Let JamLoop bring the power of the web to you. Just type in the keywords, and let JamLoop’s search engine do the work. JamLoop indexes content from multiple classifieds sites, used equipment stores and new equipment retailers for its users to bring results to a single destination using a customized spidering technology. JamLoop shows you relevant location-based results using a mashup of IP2Location's innovative geolocation solution in conjunction with the Google Maps API. To help make JamLoop’s ideas a reality we used a...read more

Categories: Web/Tech

November 8, 2007

Installing ImageMagick with FreeType, PNG, JPEG, AI and EPS Support

Often a client will request some type of image manipulation performed server side to support their application. A common example of this is the simple 'add watermark' function that you see on so many sites, but the examples can quickly get more complicated including items such as sharpen, add text, overlay images, overlay eps drawings etc... For more complicated image manipulation applications you need to move to a larger solution than the built-in image handlers in most languages. For just such an occasion we settled on the ImageMagick library, with associated extensions. We are communicating with ImageMagick using php and the image magick libraries, but the focus of this article is the ImageMagick setup itself. Generally, we found the environment...read more

Categories: Web/Tech

October 29, 2007

Adobe AIR bus tour, Toronto stop

I recently attended the Adobe OnAIR Bus Tour when it passed through Toronto. The tour was an event dedicated to teaching developers more about the new Adobe Integrated Runtime (AIR), which is currently still in beta. It was an interesting day of presentations, as well as forays into the realms of Halo 3 and Guitar Hero II. The day started with a long line (doesn't everything in a big city involve long lines?), but we were eventually herded through the front doors of the Guvernment nightclub and given our loot bags. In addition to the standard bundle of marketing material, there was a t-shirt, a CD containing Adobe AIR, documentation and numerous example applications which ran on AIR (along with...read more

Categories: Web/Tech

October 1, 2007

Another opinion on the OSGi/JSR 277 debate

I recently posted an article about the debate surrounding JSR 277 and OSGi. Some quick background on the debate is: OSGi (a.k.a. JSR 291) is a successful technology for packaging up Java software in a modular manner, and it has over 8 years of development behind it. It is also used in popular products like Eclipse and WebSphere Sun is trying to develop a technology for adding components in a modular manner to the JVM - this is being handled as part of JSR 277, which is expected to be in Java 7 There are two OSGi experts on the JSR 277 expert group At this point, the rest seems obvious - Sun will utilize the experience and learning of...read more

Categories: Web/Tech

Privacy Policy| Sitemap| Contact Us

Copyright 2002-2007 Codesta LLC. All rights reserved.