Archive for December, 2009

Google Chrome for Mac OS X is Finally Out

Tuesday, December 8th, 2009

Well... I've sneaked a few developer builds of Google Chrome for Mac OS X, but today I finally got an email from the Google Chrome for Mac team. Now when you go to the Chrome web page it detects that you're on a Mac, and gives you the option to download it for Mac OS X 10.5 or later. It's still 32-bit, but I guess that's not a terrible thing as it's not putting anything into the kernel, which would be a big no-no to getting my laptop to boot 64-bit.

The about page is very nicely done, and it appears to borrow heavily from the Windows versions for the UI, but there are several things that are very Mac-specific, and that's nice to see. Well... I'll be checking it out, but I'll be stunned if it beats Safari. Still, nice to see.

About Google Chrome

I had to laugh at the email because it listed a "few fun facts" about the Google Chrome for Mac team:

Here are a few fun facts from us on the Google Chrome for Mac team:

  • 73,804 lines of Mac-specific code written
  • 29 developer builds
  • 1,177 Mac-specific bugs fixed
  • 12 external committers and bug editors to the Google Chrome for Mac code base, 48 external code contributors
  • 64 Mac Minis doing continuous builds and tests
  • 8,760 cups of soft drinks and coffee consumed
  • 4,380 frosted mini-wheats eaten

it was the last item - the Frosted Mini-Wheats that made me laugh! It's what I have every morning at work. What a gas!

Creating User Subscription System

Monday, December 7th, 2009

Today I spent a good bit of the day trying to create a nice, user subscription system for my alerts. I needed it to be clear, but easy to use. With the database schema I had, I needed to use a left outer join to get all the alerts the user could possibly subscribe to. It was pretty fun to get it all worked out.

The core was a simple stored procedure that pulled all the data into a nice table:

  CREATE PROCEDURE dumpUserAlerts
      @username VARCHAR(80)
  AS
  BEGIN
      -- make a table to hold a little more than we'll be needing
      CREATE TABLE #everything (
          name        VARCHAR(32),
          description VARCHAR(8000),
          portfolios  VARCHAR(4096),
          chat        bit,
          email        bit
      )
      INSERT INTO #everything
          SELECT a.name, a.description, a.value AS portfolios, r.chat, r.email
            FROM ( SELECT fa.alertID, fa.name, fa.description, fp.value
                     FROM Alerts fa, AlertParams fp
                    WHERE fa.active=1
                      AND fa.alertID=fp.alertID
                      AND fp.name='Portfolio'
                      AND fp.setName='default' ) a
           LEFT OUTER JOIN AlertRecv r
            ON r.username=@username
            AND r.setName='default'
            AND r.alertID=a.alertID
 
      -- update the wildcard portfolios to something readable by the user
      UPDATE #everything
         SET portfolios='[All Portfolios]'
       WHERE portfolios='[*]'
 
      -- now let's add the portfolios to the end of the description
      UPDATE #everything
         SET description=description
                        + '<br><p class="hilite">'
                        + REPLACE(portfolios,',',', ')
                        + '</p>'
 
      -- now let's show the user what they were meant to see
      SELECT Name, Description, Chat, email AS Mail
        FROM #everything
  END

What I really like about the way this is coming together is the coordination of the SQL stored procedures, the Java Tomcat server (with its' servlets), and the web GUI with JavaScript. At my last job I did something like this, but not to this extent. I didn't have the nice JavaScript front-end... OK, I did, but the project didn't make use of it.

What I like is that I have three places to put code: the JavaScript, the Java, and the SQL. This means that I can distribute a solution in the most efficient manner possible. I can make use of the fact that the SQL can be changed independently of the Java or JavaScript, and that makes it a very powerful tool.

This is just how I like to code - across the entire system. Not just in one little slice. The code above was then massaged by the Java to remove NULL values and put in HTML INPUT check boxes and then using the onChange tag, these were able to send back messages to the same database to effect the change of the subscriptions.

Very nice.

Converting Java Properties Config to a Database Schema – Part II

Friday, December 4th, 2009

Today I spent time taking the database schema I made yesterday and making it look like a Java Properties file so that I could easily create Properties objects and then feed them to the alerts I had and not have to worry about changing the rest of the code. The problem is, the database schema was logical, but it wasn't anything like what the previous Properties file layout looked like.

The ChatTo and MailTo properties were at the same level as the rest of the parameters, but in the new database schema, it's an entirely new (and separate) table, with links to an existing table for the email address. It's different, all right. After a little messing around in SQSH, I came up with the following stored procedure that takes a set name and returns two columns - keys and values, and from this, I can easily feed a newly created Properties object and we're good to go.

  CREATE PROCEDURE sp_getProperties
      @setName VARCHAR(80)
  AS
  BEGIN
      -- get an answer table to hold the whole lot of it
      CREATE TABLE #answer (
          name    VARCHAR(256),
          VALUE   VARCHAR(4096)
      )
 
      -- get the list of the alertIDs that are in this set
      CREATE TABLE #interesting (
          alertID     INT
      )
      INSERT INTO #interesting
          SELECT DISTINCT a.alertID
            FROM Alerts a, ALertParams p
           WHERE p.setName=@setName
             AND a.alertID=p.alertID
             AND a.active=1
 
      -- get the list of all active, interesting alerts
      DECLARE @id INT,
              @val VARCHAR(4096)
      SELECT @val=''
      SELECT @val=@val+CASE WHEN @val='' THEN '' ELSE ';' END+a.name
        FROM Alerts a
       WHERE a.active=1
      -- ...and now that it's built, put it into the answer
      INSERT INTO #answer
          VALUES('Alerts', @val)
 
      -- run through them all, processing each in turn
      DECLARE actives cursor FOR
          SELECT alertID FROM #interesting
      OPEN actives
      fetch NEXT FROM actives INTO @id
      while @@fetch_status = 0
      BEGIN
          -- get the ClassName for this active guy
          INSERT INTO #answer
              SELECT 'Alert.'+a.name+'.ClassName' AS name, a.classname AS VALUE
                FROM Alerts a
               WHERE a.alertID=@id
 
          -- get the VersionID for this active guy
          INSERT INTO #answer
              SELECT 'Alert.'+a.name+'.VersionID' AS name, a.versionID AS VALUE
                FROM Alerts a
               WHERE a.alertID=@id
 
          -- get the bulk of the parameters for the alert...
          INSERT INTO #answer
              SELECT 'Alert.'+name+'.'+p.name AS name, p.value AS VALUE
                FROM Alerts a, AlertParams p
               WHERE a.alertID=@id
                 AND a.alertID=p.alertID
                 AND p.setName=@setName
 
          -- get the ChatTo list for this active guy
          SELECT @val=''
          SELECT @val=@val+CASE WHEN @val='' THEN '' ELSE ';' END+u.username
            FROM AlertRecv r, Users u
           WHERE r.alertID=@id
             AND r.username=u.username
             AND r.chat=1
             AND r.setName=@setName
          -- ...now that it's built, insert it into the answer
          IF @val<>''
              INSERT INTO #answer
                  SELECT 'Alert.'+a.name+'.ChatTo' AS name, @val AS VALUE
                    FROM Alerts a
                   WHERE a.alertID=@id
 
          -- get the MailTo list for this active guy
          SELECT @val=''
          SELECT @val=@val+CASE WHEN @val='' THEN '' ELSE ';' END+u.username
            FROM AlertRecv r, Users u
           WHERE r.alertID=@id
             AND r.username=u.username
             AND r.email=1
             AND r.setName=@setName
          -- ...now that it's built, insert it into the answer
          IF @val<>''
              INSERT INTO #answer
                  SELECT 'Alert.'+a.name+'.MailTo' AS name, @val AS VALUE
                    FROM Alerts a
                   WHERE a.alertID=@id
 
          fetch NEXT FROM actives INTO @id
      END
      close actives
      deallocate actives
 
      -- now return this to the caller
      SELECT name, VALUE FROM #answer
  END

Now while I'll be the first to admit that this is not the most efficient way of getting the data into a format that I can easily parse, it's pretty fool-proof. I wanted to make the Java parsing as simple as possible, and this does the job - a simple table with two columns - names and values to put in the map. Can't get any simpler than that.

I will say that some of the parts that I think are clever are the building of the strings that are really individual rows in the database - like the list of active alerts, or the list of ChatTo or MailTo recipients:

      SELECT @val=''
      SELECT @val=@val+CASE WHEN @val='' THEN '' ELSE ';' END+a.name
        FROM Alerts a
       WHERE a.active=1
      -- ...and now that it's built, put it into the answer
      INSERT INTO #answer
          VALUES('Alerts', @val)

We know that the select statement contains an implicit loop, and by using the case statement in the select, we get the delimiters in the right place - as opposed to an unnecessary trailing, or leading, one. I have to give credit for the use of the case to a co-worker. Pretty nice and concise.

With this, I'm able to finish the replacement of the Properties file with a database schema, and that's what I needed. In the coming days I need to make an editor in my web app, and then put a user-serviceable subscription page together so that the users can update what they get on their own. That will be a nice win for the web app.

Miro 2.5.4 is Out

Friday, December 4th, 2009

I keep hoping that Miro will get a little more mainstream, but I think it's going to be a lot like Public Television - interesting, and educational, but not necessarily entertaining. Still, it's nice to see them making progress, and there's always time to learn something new on PBS. 🙂

Java for Mac OS X 10.6 Update 1 on Software Updates

Friday, December 4th, 2009

Apple has released JDK 1.6.0_17 for Mac OS X. 'Nuff said. Have to get it.

Converting a Java Properties Config to a Database Schema

Wednesday, December 2nd, 2009

Today I started the work on a conversion in my webapp's codebase from a Java Properties file-based system to a database-based system because the users were asking for an editing page for their alerts. It's understandable, I created the alerts config simply thinking that I'd be the one adding and removing people from the lists, but in the end, the users wanted to be able to control this themselves. While it's possible to have them edit the Properties object data and then write it out to the file system, that makes for a mess in trying to read it. I've run into that before.

So I needed to make the conversion without a huge impact to the codebase. I started out with a pretty standard Java Properties file:

  # list all the alerts by name that we'll be using
  Alerts=One;Two;Three
 
  # this is the config for alert 'One'
  Alert.One.ClassName=com.bobbeaty.alerts.FlipFlop
  Alert.One.VersionID=20091029
  Alert.One.SilenceInMins=10
  Alert.One.AlertExpr=(pnl > 50000)
  Alert.One.Message="The p/l exceeds $50,000: " + pnl
  Alert.One.ChatTo=drbob;liza;joe
 
  # this is the config for alert 'Two'
  Alert.Two.ClassName=com.bobbeaty.alerts.BaseAlert
  Alert.Two.VersionID=20091111
  Alert.Two.HoldTimeInMins=5
  Alert.Two.AlertExpr=(leadFuture == leadFutureLast)
  Alert.Two.Message="The lead future has not moved in 5 mins: " + leadFuture
  Alert.Two.ChatTo=drbob;liza
 
  # this is the config for alert 'Three'
  Alert.Three.ClassName=com.bobbeaty.alerts.BarrierBreach
  Alert.Three.VersionID=20091011
  Alert.Three.States=T;T;T;T;T;F;F;F;F;F
  Alert.Three.n=-5..5
  Alert.Three.AlertExpr=(pnl > n*50000)
  Alert.Three.Message="The p/l exceeds $50,000: " + pnl
  Alert.One.ChatTo=joe;mimi;nina

OK, they're all made up, but the idea is that this system is very flexible, and while a database-system will be nice to have, it's a lot more work to put together than this. There's the fact that this is an a corporate environment, so you can't just make databases ad hoc, and such.

I thought (briefly) about using SQLite3, but it's Java API is JNI, and that's something I'm just not ready to do. I could have used H2, but that system makes multiple files for a single database, and that's just a little too much clutter for me. I wanted something simple, and easy to work with.

In the end, I had to give in and go with the corporate 'standard': MS SQL Server. OK, it's life. But I then needed to try to come up with a way to easily edit the ChatTo arguments of each alert, and there was also a MailTo property that I just didn't include in this example. Meaning: I couldn't just have a single table with two columns and leave it at that. I needed to have something that was a little better designed than that.

So I started looking at the ways in which I was going to need to use this data. First, I needed to have something that was going to hold the basics of the alert:

  CREATE TABLE Alerts (
    alertID        INT IDENTITY,
    name           VARCHAR(132) NOT NULL,
    description    VARCHAR(4096) NOT NULL,
    classname      VARCHAR(256) NOT NULL,
    versionID      INT NOT NULL,
    active         bit NOT NULL DEFAULT 1,
    CONSTRAINT Alerts_PK PRIMARY KEY (alertID)
  )

I toyed a lot with whether or not to include more of the common attributes, like AlertExpr and a few others, but in the end, I didn't want to have these records fiddled with much. I thought it was better to just activate/deactivate them and that's it. The rest should be set once and then left alone.

Clearly, the versionID is going to be updated, but the reason it's in the table, and Java Properties scheme originally, is that I would make changes to the alerts, and the state would be saved so that on restart it didn't re-alert for the same conditions. This meant that I needed to know when the saved state was stale, and that's where the versionID comes in.

If the versionID changes from what's saved for the named alert, we assume that there's something about the alert that fundamentally changed. That means that the state can't be trusted, and so has to start from scratch. It's just like Java's RMI serialVersionUID, in that regard. Which is, of course, where I got the idea.

The bulk of the parameters for the alerts could be held in a very simple table:

  CREATE TABLE AlertParams (
    alertID        INT,
    setName        VARCHAR(80) NOT NULL,
    name           VARCHAR(132) NOT NULL,
    VALUE          VARCHAR(4096),
    CONSTRAINT AlertParamsPK PRIMARY KEY (alertID, setName, name),
    CONSTRAINT AlertParams_ID_FK FOREIGN KEY (alertID) REFERENCES Alerts (alertID)
  )

Here, I'm just going for the basic key/value pairs that comprise the lion share of the configuration parameters for the alerts. Pretty simple stuff. The last table was a little more of a toughie.

We already had a permissioning table in the database, which had the primary key field of username, which the web app would get from the logged-in user - or a login box, whichever was easier to get. I decided to expand this table and add in the email address of this user so that we could use this one table as the 'target locator' for the alerts. The chat id was the username, and with the email address, all I needed to add was a table that cross-referenced the alerts to the users, and how they wanted to be notified:

  CREATE TABLE AlertRecv (
    alertID        INT,
    setName        VARCHAR(80) NOT NULL,
    username       VARCHAR(80) NOT NULL,
    chat           bit NOT NULL DEFAULT 0,
    email          bit NOT NULL DEFAULT 0,
    CONSTRAINT AlertParamsPK PRIMARY KEY (alertID, setName, username),
    CONSTRAINT AlertParams_ID_FK FOREIGN KEY (alertID) REFERENCES Alerts (alertID)
  )

With this, we could easily get the list of active (or inactive) alerts and a description. That was important to me for the web page. Also, I needed to be able to easily edit the alerts the users were getting, and with this simple cross-reference table, I can build a servlet that will quickly check/uncheck the bit fields in the table for the given user/alert combination.

It took me several hours to come to this design. I tried to simplify it to two tables, or one, and each time I ended up with much harder maintenance tasks to do the things I could have easily done with a text editor and the Properties file. I was really trying to make it pretty simple to get this converted as I didn't have a lot of time to make the conversion and be sure it worked.

After playing with it in my head (and adding in all the values from the Properties file), I was convinced that it would work. Now all I had to do was to build the SQL to get the data out of the tables in a way that it's easy to build a Java Properties object, and then test that I actually had something that worked.

Upgraded to WordPress 2.8.6 at HostMonster

Wednesday, December 2nd, 2009

This morning I was playing with the new WordPress 2 iPhone app - putting my blogs into the app, and realized that I didn't have written down the password to my old journal from UBS. This was a mistake, because I remember forgetting it, then changing it and then today I found where I'd written down the coded message that was the password, but forgot the changed one.

Thankfully, the way to change the password is really very simple - assuming that you have access to the databases that drive the system... and I do. So I changed it back to the original value and left it at that. Now I'm ready to play with the new iPhone app, and it works. Nicely, in fact.

While I was at HostMonster, I noticed that WordPress 2.8.6 was out, and so I upgraded my installs to the latest stable release. They had 2.9b1, but I'm not that hyped to use the beta - I can wait.

It's great tools, well put together, and hosted by a great company. Love this set-up.

How Possible is it to Get Honest Opinions at Work?

Tuesday, December 1st, 2009

I've been asked to give a assessment of the current technical state of the Shop I've been at for about nine months now. In general, I'd say that's not enough time to really understand the ins and outs of the place, but they asked, and so I have to give it my best shot.

As I started formulating my answers to their questions, I started to really wonder if they wanted my answers. I mean, really... I was thinking that many of these questions were the equivalent of re-arranging desk chairs on the Titanic. They were questions, yes, and they had answers, yes, but they were all the wrong questions to be asking. I kept trying to think about a way to phrase the answers to give a politically-correct answer while still expressing my opinions, and just couldn't.

This, of course, made me want to not answer the questions, but that really wasn't the answer, either, as they didn't ask everyone the same questions, so they were clearly looking for my take on the issues, and that meant that I really had to answer these questions, like it or not.

So I decided to take another tack - the direct approach: answer their questions stating that it's not about these details they should be asking, but the bigger questions like centralized versus distributed and diverse... groups providing fixed services, or groups assisting other groups in what they want to do... there are a lot bigger issues that need to be answered before you can really get a good idea of what your current state is.

For example, if you are moving towards a distributed development model, then the only measure that matters is: are the users getting the tools they ask for? If the answer is Yes, then it doesn't matter that there are six different types of databases in use, or that there are a dozen different languages on three different operating systems. It's all secondary to getting the product to the end-user.

But if you're going for a more centralized approach with standards for linux workstations and servers, and a Database Team where lots of decisions are made, then you're not just looking at the end result - you're looking at how the systems work, are deployed, supported, updated... all the things that large, centralized IT departments look at.

Two very different ways of working, and two entirely different ways of delivering a product to a customer. But both can work - it's just important to remember which one you're attempting so as not to confuse the two.

When I looked back at what I'd written, I realized that I really didn't know if they were interested in reading this. Maybe so, maybe not. I decided to end it with a simple closing paragraph

Like I said, I'm not sure this is really what you guys, and the guys asking you to compile this list were looking for. I've only been here nine months. That's not long enough to know a lot of people, or really understand the way things are done, and why.

But I've done my best at giving you an honest, clear, direct answer to your questions. I hope it helps. If not, throw it out. It's all just an opinion.

And in the end, I think it's nearly impossible to get a really un-biased, honest opinion of the technological state of things from within. Everyone has their biases, and while we might try to really remove them, they're there, anyway. I guess it's going to have to do for the list they are compiling. It's the best I can do.

Xcode 3.2.1 is Out on Software Updates

Tuesday, December 1st, 2009

I was really surprised this morning to see that Xcode 3.2.1 was out on Software Updates. The notes on the developer web site say:

Xcode 3.2.1 is an update release of the developer tools for Mac OS X. This release provides bug fixes in gdb, Interface Builder, Instruments, llvm-gcc and Clang optimizer, Shark, and Xcode and must be installed on Mac OS X 10.6 Snow Leopard and higher.

so it appears to have updated components of the Xcode suite as opposed to actually updating the Xcode GUI app. Interesting.

Certainly had to get that.