Friday, April 30, 2010

Delayed environment variable expansion in Windows

Today I got to learn the wonders of "Delayed Environment Variable Expansion" in windows.... this one really hurt my head :)

As a basis for this entry I'm going to use information from another article as the starting point (http://stackoverflow.com/questions/305605/weird-scope-issue-in-bat-file). After I read the article for the first time I still wasn't sure what was going on so I'm going to explain in a little more detail below.

For the example we'll start with the code below:

set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" @echo If you see this, it worked
)

When I first looked at the code I throught that I would see "If you see this, it worked" in the command prompt window when the code is ran, but unfrotunately that is not how bat files work... (this really surprised me).

What is really happening is as follows:

1) Windows loads "before" into the variable
2) Windows loads the entire if into memory
3) Windows sets all instances of %VAR% to before
4) when the code hits the following line "if "%VAR%" == "after" @echo If you see this, it worked" the value in %VAR% is "before" because the value was replaced prior to the "set VAR=after" statement and %VAR% will only be equal to "after" once the program exits the if statement's scope

To take this example one step further I added "@Echo %VAR%" at the end of the code (after the if statement) as described below.

set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" @echo If you see this, it worked
)
@Echo %VAR%


When the code above executes you get the following output to the command window:

if "before" == "before" (
set VAR=after
if "before" == "after"
)
after

Please note that once the program gets out of the if statement scope the value in %VAR% IS set to "after".


From the articles I've read thus far the system works like this because this is how it was originally implemented. However, there is something called "delayed environment variable expansion" in windows that makes the command file work as we expect. The code with delayed environment variable expansion implemented is provided below:

SETLOCAL EnableDelayedExpansion
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" @echo If you see this, it worked
)
@Echo %VAR%


The most important difference with the code provided above is the statement "SETLOCAL EnableDelayedExpansion". When this is set all variables marked with '!' instead of '%' are evaluated at runtime when they are used rather than when the block of code they are in is evaluated.

So with that being said, when the code above is executed you'll get the following output.

set VAR=after
if "!VAR!" == "after"
)
If you see this, it worked
after

Friday, April 23, 2010

Spring 3.0 Restful Web Services -- Flickr Style URLs

I just recently started to use the Spring Framework to create RESTful web services. We chose the Spring Framework because we are going to run in production with WebSphere v6.1 without any of the feature packs installed (ugh... one of these days maybe we'll get a modern application server) and WebSphere v6.1 didn't support the full J2EE spec for Java 1.5 when it was released. In order to support features like EJB 3.0 you must install the feature packs, but I digress.

Also, when I started to use the Spring Framework all of the RESTful web service tutorials used URL templates like the following where 1234233 is the index that identifies a particular book.

http://www.mydomain.com/books/1234233

However I wanted to do something like this:

http://www.mydomain.com?method=genInfo&bookId=1234233

I'm not sure why all of springs tutorials used the first style, but a lot of the RESTful web services that I looked into (ex: Flickr and Twitter) both support the second style.

So, with that being said, how do you create a class that allows you to use URL templates like Twitter.

@Controller
@RequestMapping(value = "/bookController.*")
public class BookController {
    @RequestMapping(value = "?method={method}&bookId={bookId}", params = {
"bookId"})
    public ModelAndView getBookDetails(
        @RequestParam("method") String method,
        @RequestParam("bookId") String bookId) {
        // business logic
    }
}

Please note the use of the @RequestParam attribute in the above example. You cannot use @PathVariable. Also, use params to help the system understand what is required or what shouldn't be there. In the following example I show how to tell the system that it shouldn't allow a particular argument.

@Controller
@RequestMapping(value = "/bookController.*")
public class BookController {
    @RequestMapping(value = "?method={method}&genre={genre}", params = {
"genre", "!publisherId"})
    public ModelAndView getBooksByGenre(
        @RequestParam("method") String method,
        @RequestParam("genre") String genre) {
            // busines logic
    }


    @RequestMapping(value = "?method={method}&genre={genre}&publisherId={publisherId}", params = {
"genre", "publisherId"})
    public ModelAndView getBooksByGenre(
        @RequestParam("method") String method,
        @RequestParam("genre") String genre, 
        @RequestParam("publisherId") String publisherId) {
            // busines logic
    }
}


From what I've found thus far if you have the code like above (pretty much allowing overloaded methods) you must tell the system not to execute the first method if it contains the publisherId parameter.  This is accomplished by using '!publisherId'.

More information about the use of params can be found at Stackoverflow and also in the javadoc for the @RequestMapping annotation.

Thursday, April 22, 2010

Making Criteria Queries Case Sensative in SQL Server 2005

I recently recieved a problem ticket at work because we were showing incorrect data for a particular user. The problem was caused by the fact that the process that creates ids considers ID1234 to be different than id1234. (one of those small requirements issues that gets missed... woops) Anyway I did some searching online and found the COLLATE keyword for SQL Server. The COLLATE keyword allows you to create a SQL statement that overrides the entire databases case sensitivity rules. I have provided an examples below:

Select * from FOO where id ='AbC' COLLATE SQL_Latin1_General_CP1_CS_AS

This would return all rows in the table that had ABC as the ID, but more importantly it would require that A and C be uppercase while b is lowercase. Please note that you could have placed the COLLATE SQL_Latin1_General_CP1_CS_AS text directly after ID instead of including it after the literal.

In addition you could do the following and use the 'IN' keyword.

Select * from FOO where id COLLATE SQL_Latin1_General_CP1_CS_AS IN ('AbC')

The above query again requires that the ID match 'AbC' using case sensative rules. However notice that the COLLATE SQL_Latin1_General_CP1_CS_AS is placed prior to the 'IN' keyword. I have not been able to get SQL Statements that include the 'IN' keyword to work if you put COLLATE SQL_Latin1_General_CP1_CS_AS after the literal.


Some resources I used during my research for this issue are: