Saturday, August 23, 2014

Accessing Yahoo BOSS APIs from a Server Application

Recently I was working on a feature that required the use of a reverse geo-coding service. For those of you that don't know, a reverse geo-coding service takes coordinates (latitude and longitude) and translates them to an address. We decided to use Yahoo BOSS's PlaceFinder service as the price was right and the free alternatives didn't provide enough quota for our purposes. But there was a problem...

OAuth.

OAuth isn't really a big deal if you're working on an application with a user interface that can react to security prompts and such, but I'm not, my application runs as a web service that does not have the opportunity to prompt the end user.

So what to do?

If you're using the JVM platform I highly recommend the Scribe library, it really does make things easier to deal with.

So anyway, let's get started...

The first thing you need to do is generate an initial access token, access secret, and an OAuth session handle (I'll explain in a bit).

Using the following Groovy script I was able to generate everything I needed:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.scribe.builder.ServiceBuilder
import org.scribe.builder.api.YahooApi
import org.scribe.model.OAuthRequest
import org.scribe.model.Response
import org.scribe.model.Token
import org.scribe.model.Verb
import org.scribe.model.Verifier
import org.scribe.oauth.OAuthService

def yahooConsumerKey = '<Your Yahoo Consumer Key>'
def yahooConsumerSecretKey = '<Your Yahoo Consumer Secret>'

def service = new ServiceBuilder().provider(YahooApi).apiKey(yahooConsumerKey).apiSecret(yahooConsumerSecretKey).build()

def requestToken = service.getRequestToken()

println "Go to the following URL and authenticate: ${service.getAuthorizationUrl(requestToken)}"

print "Enter the verification code >> "

def verifier = new Verifier(System.console().readLine())

def accessToken = service.getAccessToken(requestToken, verifier)

println "Access Token: ${accessToken.token}"
println ""
println "Access Secret: ${accessToken.secret}"
println ""
println "OAuth Session Handle: ${accessToken.rawResponse.split('&')[3].split('=')[1]}"

The access token and access secret are only good for an hour, but that's OK because we'll be able to refresh the access token and secret using that OAuth session handle that we extracted.

Now that we've gathered our tokens and secrets and sessions handles (OH MY!), we need a method that will retrieve a fresh token for us when appropriate. I have the following method setup so that if a retrieved token is more than thirty minutes old we go ahead and refresh.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.example.yahooboss

import org.joda.time.DateTime
import org.joda.time.DateTimeConstants
import org.scribe.builder.ServiceBuilder
import org.scribe.builder.api.YahooApi
import org.scribe.model.OAuthRequest
import org.scribe.model.Token
import org.scribe.model.Verb
import org.scribe.oauth.OAuthService

class YahooBossExampleUtil {

    // Timestamp starts at zero so the first call to the method will always refresh the token.
    static long yahooAccessTokenTimestamp = 0

    // The following values should never change and could be put in a config file or something.
    static final String yahooConsumerKey = '<Your Yahoo Consumer Key>'
    static final String yahooConsumerSecretKey = '<Your Yahoo Consumer Secret>'
    static final String yahooSessionHandle = '<Your Generated Yahoo Session Handle>'

    // These values will change.
    static String yahooAccessToken = '<Your Generated Yahoo Access Token>'
    static String yahooAccessSecret = '<Your Generated Yahoo Access Secret>'

    static final long maxTokenDuration = DateTimeConstants.MILLIS_PER_MINUTE * 30

    // Generate a new token at least every 30 minutes. 
    static synchronized Token getYahooAccessToken() {
        def tokenAge = new DateTime().getMillis() - yahooAccessTokenTimestamp

        if (tokenAge >= maxTokenDuration) {
            def service = getYahooOAuthService()

            def accessToken = new Token(yahooAccessToken, yahooAccessSecret)

            def request = new OAuthRequest(Verb.GET, "https://api.login.yahoo.com/oauth/v2/get_token")
            request.addOAuthParameter("oauth_session_handle", yahooSessionHandle)

            service.signRequest(accessToken, request)

            def response = request.send()

            def yahooApi = new YahooApi()

            def refreshedAccessToken = yahooApi.getAccessTokenExtractor().extract(response.getBody())

            yahooAccessToken = refreshedAccessToken.getToken()
            yahooAccessSecret = refreshedAccessToken.getSecret()

            yahooAccessTokenTimestamp = new DateTime().getMillis()
        }

        new Token(yahooAccessToken, yahooAccessSecret)
    }

    static OAuthService getYahooOAuthService() {
        new ServiceBuilder().provider(YahooApi).apiKey(yahooConsumerKey).apiSecret(yahooConsumerSecretKey).build()
    }
}

Notice on line 38 in the above example we're adding an OAuth parameter called "oauth_session_handle" and providing the session handle that we gathered using the script from the beginning of the post. The session handle allows us to identify the app as having been previously authorized even if the access token and secret have expired.

At this point we have everything we need to finally make a request for data from one of the Yahoo BOSS APIs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
static String getAddressFromYahoo(double latitude, double longitude) {
    // The request URL needs to be URL encoded or else you'll get a "signature invalid" error.
    def url = "http://yboss.yahooapis.com/geo/placefinder?location=${latitude}%2B${longitude}&gflags=R"

    // The getYahooOAuthService() method from the previous example.
    def service = getYahooOAuthService()
    def request = new OAuthRequest(Verb.GET, url)

    service.signRequest(getYahooAccessToken(), request)

    def response = request.send()

    if (response.code == 200) {
        def xml = new XmlSlurper().parseText(response.body)
        def firstResult = xml.placefinder.children().first().result

        return "${firstResult.line1}, ${firstResult.city}, ${firstResult.statecode} ${firstResult.uzip}, ${firstResult.countrycode}"
    }

    ''
}

Pay special attention to the comment on line 2 in the above example, it's super important. If the request URL isn't properly escaped (URL encoded) you'll get an error response similar to the following:


1
2
3
4
5
<?xml version='1.0' encoding='UTF-8'?>
<yahoo:error xmlns:yahoo='http://yahooapis.com/v1/base.rng'
  xml:lang='en-US'>
  <yahoo:description>Please provide valid credentials. OAuth oauth_problem="signature_invalid", realm="yahooapis.com"</yahoo:description>
</yahoo:error>

So that's about all there is to it.

Still not a big fan of dealing with OAuth, though after this implementation and writing this post it doesn't seem that big a deal.

Happy coding!

Wednesday, April 10, 2013

Going Green - Hiring a Junior Developer

So you've decided to expand your team, and you're going to do so by adding an entry level developer. Good for you. Here are some thoughts and suggestions that might be able to help.

Interview

When interviewing a candidate for a junior level role what exactly should you be looking for? Well, by definition they don't have very much experience so that's out. If you're lucky they have contributed to, or created a few open source projects; but that isn't likely. Really at this point you're looking for a basic understanding of programming concepts, your programming language of choice, and whether or not the candidate has a personality.

Optimally, you should have a few members of your team sit down with the candidate and have an informal discussion as a way of determining whether or not the candidate fits with the group on a social level.

Algorithmic Code Test

Since you are specifically looking to hire a junior level developer, you probably won't have much success with asking complicated questions about your programming language or platform of choice. In my opinion you should stay away from having a candidate do the 'Fizz Buzz' problem or writing a Fibonacci number generator as those exercises are played with the solutions well known. Instead try the following questions which are designed to be simple enough to work on a white board and require no knowledge of a specific programming language. 

Given the following list of numbers: [1, 2, 3, 4, 5]
  1. Write a function that will sum the numbers present in the list.
  2. Write a function that will sum only the odd numbers present in the list.
  3. Write a function that will sum only the highest and lowest numbers present in the list. 
  4. Write a function that utilizes recursion to sum the numbers present in the list. 
Keep in mind the idea here is to see how the candidate attempts the problems above. Yes these questions seem a little too easy, but I've seen a few candidates really struggle to answer them. 

Velocity

So, you've decided that you are going to hire the candidate as a junior developer. There are some things that you need to keep in mind.

The new developer isn't going to add to your team's velocity, at all. In fact you can expect to see a decline in velocity as the rest of your team (hopefully) rally's around the new hire in an effort to get them up to speed as quickly as possible.

Be realistic in your expectations on this because it's probably going to be the new reality for several sprints.

Assignments

Junior level developers should start small. Like I said above, you shouldn't expect or demand that they add to your teams project velocity at first. One idea is to assign the developer a few technical debt items (you do keep a backlog of those right), and submit their changes to the team for review. This is a nice gentle way to introduce the new developer to your code base and processes without overwhelming them with a major feature.

Side note, don't just sit the new hire down in front of a computer and have them "read code" all day. That's just asinine. 

Conclusion

The addition of a junior developer can be a great boon for your team. The act of teaching allows your more senior team members to gain a bit of supervisory experience as well as a bit of reinforcement for their own knowledge. All that being said, remember, it is unfair to your team as well as the new junior developer to expect too much too soon.

Sunday, December 30, 2012

Establishing a Linked Server to Google Cloud SQL

Need to create a linked server to a Google Cloud SQL instance? I did. Here's how I did it.

For the purposes of this post I'm using Microsoft SQL Server 2008 Express. However I have reproduced the results here using Microsoft SQL Server 2005 Enterprise Edition as well as Microsoft SQL Server 2012 Express.

What you need.

On the server you intend to establish the linked server from you need to install the following software in the order listed.

Java 6 JDK - link

Google Cloud SQL Tools - download 

OpenLink Single-Tier ODBC to JDBC Bridge (Lite Edition) - This isn't freeware. - link 

What you need to do.

Obviously you need to install the Java 6 JDK before you do anything. So do that. 

Next, extract the Google Cloud SQL tools to a permanent directory (no bullshit temp directories or anything). For the purposes of this document I am going to assume that you placed the tools in a directory called 'C:\google_cloud_sql\'.

Now you need to create/alter some system level environment variables. 

First you need to ensure that 'google_sql.jar' is present in your 'CLASSPATH' variable. On my computer/server I didn't have a 'CLASSPATH' variable so I had to create it. Here is a screen shot of how I have mine setup:


Now you need to verify that your 'PATH' environment variable is correctly setup. I had to add the following directory to my 'PATH' variable:

C:\Program Files\Java\jre6\bin\server

Having the above directory in your 'PATH' will avoid errors like 'jvm.dll cannot be found'.

Now we need to setup the Google Cloud SQL tools. If you're at all familiar with the Google Cloud SQL tools, you know that you'll need to get an OAuth token in order to be able to connect to a Cloud SQL instance. In order for this magic to work you'll need to setup the OAuth token using the account that the SQL Server service is running under. This is because the OAuth token is cached in the registry under HKEY_CURRENT_USER.

Next we setup the bridge to the Google Cloud SQL JDBC.

I'm assuming at this point that you've already installed the bridge software from OpenLink. So now we are going to setup an ODBC...

Complete the following steps:


Open the 'ODBC Data Source Administrator'.

Click on the 'System DSN' tab.

Click the 'Add' button.

Select the driver labeled 'OpenLink Lite for JDK 1.5 [6.0]' and then click 'Finish'.


Give a name to the datasource you are creating.


Specify the JDBC driver and URL string.


The JDBC driver is: com.google.cloud.sql.Driver
The URL string is: {jdbc:google:rdbms:put-in-you-cloud-sql-instance-name}

Click 'Next' on the wizard until the last step of the wizard. You should have the opportunity to test the connection. Go ahead and test to ensure that we've done everything right up until this point.

After you verify that the connection has succeeded. Reboot.

Seriously, reboot. If you don't you'll most likely encounter good ol' system error 126 when you attempt to setup the linked server.

Now we'll setup our linked server.

I'm assuming that you know how to get to the dialog that allows you to specify a new linked server. So once you're there you need to obviously name the linked server, specify the provider as 'Microsoft OLE DB Provider for ODBC Drivers', and then specify the product name and data source as whatever you called your bridged ODBC connection to your cloud SQL instance.



Assuming that my instructions aren't complete shit, and assuming that you followed them correctly, you should be able to click 'OK' and have a functioning linked server to your Google Cloud SQL instance.

Next Steps

Now that you have a handy dandy linked server, you are probably going to have a hankering to run a query against it.

Usually when you query against a linked server you use a 4 part name (linked server, database, schema owner, table name) in order to address a table. Problem is, that type of query isn't supported so we'll have to use OpenQuery. 

Never used OpenQuery? Here is a quick lesson. Let us suppose that you named your linked server 'GCS'. Let us also suppose that on your Cloud SQL instance you want to query a database called 'Example' and a table called 'Plateau'.  Your query (using OpenQuery) would look like this:

SELECT * FROM OPENQUERY(GCS, 'SELECT * FROM Example.Plateau;')
Pay close attention to the fact that the name of the linked server is not enclosed in quotes, and to the fact that the query that I specified against the 'Example' database has a semi-colon at the end. Remember, that the queries that you specify inside of OpenQuery must be syntactically identical to the queries that you would normally execute against Google Cloud SQL or MySQL.

Any questions, concerns, cusses, or discusses? Put them in the comments.

P.S. If anyone knows of a free JDBC to ODBC bridge let me know. I'd like this solution to be free...

Thursday, January 5, 2012

Moving On

There comes a time in every developer's life when it's time to move on. Sometimes it's your choice, sometimes the choice is made for you, either way you're going. This post is about how I've dealt with both situations.

Your Choice

So you've decided it's time for you to move on.
There are a few questions that you need some solid answers to first.
  1. Why are you leaving?
    Think hard about the answer to this question. If the only reason you're leaving is for more money you should do some research to ensure your not already making a "fair" salary. Also, I speak from experience, money can't make you happy if you simply dislike what you do.
  2. What are you looking for?
    Think big here! What is missing from your current environment that you'd like to see in your next? Bigger team? Larger databases? More time off? Whatever it is, make note, because the answer to this question will help you make the best move. Sounds obvious I know, but sometimes the salary offer alone is enough to make you want to say yes. Keep the answer to this question as a benchmark to objectively judge how good any potential job offer is.

Not Your Choice

The choice has been made for you, it's time for you to go.
Whether you've been laid off or fired one thing is almost certain, you'll think they are fools. But guess what, it doesn't matter, you're still not employed and the company will survive without you. The quicker you accept this the better off you'll be.
Some people might tell you that you should start your job search immediately after being handed your walking papers. I would suggest (if possible) taking a day or two to decompress and try to shake any negative feelings about your situation out of your system, they are not helpful when trying to find a new gig.
Your spouse may take your new situation harder than you are. But that's OK. It's your job to support them no matter what. I found that by reassuring my wife that everything was going to be OK actually helped me realize that everything was going to be OK. DO NOT resent your spouse for not coddling your bruised ego, it's possible they are scared, so just relax. Keep cool and when this is all over you'll find that you have a stronger relationship.
Keep moving. Before and during your job search you have to keep moving. Do something! Write some code, write a blog post, go pick up litter, just don't sit idle. I found that messing around with the Project Euler questions was a great way to keep me occupied between calls with recruiters and job interviews.
Sleeping. I should warn you, your quality of sleep will go down the metaphorical shitter. Just accept it. No extra sleeping pills or booze for you. The last thing you need is a drug or alcohol problem. Don't worry though, once you have an offer in hand this to shall pass.

Resume

You've been keeping this thing updated right? I didn't think so.
One thing I've found after having A LOT of people look at my resume is that everyone has their own idea of what makes a great resume. I took the "action->impact" approach to writing my resume. Basically, what did you do and what impact did it have. Here's a sample from my resume:
Designed and implemented a corporate directory XML application for our Cisco Call Manager deployment. The directory was populated by integrating with Active Directory for employee and department information as well as the Call Manager's internal LDAP server for phone number and extension information. The end result was that a user was able to browse through the corporate directory by department on their phone.
Wordy? Sure is, but most people that reviewed my resume didn't seem to mind. Just don't go overboard, no one is going to read a twenty page resume. Listen to your gut here, if you worked with a one hour photo mini-lab ten years ago a prospective employer looking for a developer probably won't care.

Job Leads

In todays job market place you'll probably have to deal with a recruiter. This isn't a bad thing you just have to be smart about it and remember one thing, you don't work for them. You are how they make money, so if they aren't treating you right, walk away.
But if you are lucky enough to have a personal network (no I'm not talking about LinkedIn, but hell it couldn't hurt), it's time to engage it. Time and again I've found that it's more about who you know than what you know. On my most recent job hunt, I had acquaintances or friends at two of the three companies. People are a little less likely to bullshit you if you have a history.

Interviewing

Depending on who you ask, this can be the most nerve wracking part of the entire process.
But it shouldn't be.
A job interview should feel like a conversation about developing software, yourself and the company you are interviewing with. You should obviously avoid using foul language, but if you can't get a good conversational flow going then you probably wouldn't fit in anyway.
In my latest round of interviews I was lucky enough to avoid bizarre interview questions. I understand the motivation behind asking someone "if they were were a breakfast cereal which one would they be", but give me a break, just because you read a book, blog post or pamphlet about this stuff it doesn't make you a PhD in behavior psychology. I digress. If you find that your interview is a series of these questions you should treat that as a warning sign.
Another warning sign. Punctuality. Remember it's not just them interviewing you. If you are scheduled for a two o'clock interview and they put you in a non-descript conference room and make you wait twenty minutes until finally the interviewer comes in with a smug look and no apology, huge warning sign.
But always be courteous. No matter how much you feel an interviewer has slighted you, it does you no favors to tell him/him what to do and where to do it.

Job Offer Consideration

So you've got at least one offer. Consider it carefully, because if everything goes the way it should you'll be there a while.
I recently had three job offers, here are some of the things that I took into consideration when making my decision.
  • Money. Obviously not the most important aspect of the job offer but still important. Think about your last company. How many merit increases in pay did you receive. It's probably the same way at the next company. All I'm trying to say is, you may not get a raise for a couple of years, so make sure you are comfortable with the number.
  • Flexibility. Does the shop you are considering require that you work 50 hours a week? Can you work from home? Can you work 7am to 4pm? These are important questions to ask, because they go towards your long term happiness.
  • Commute. The shorter the better. Need I say more?
  • Benefits. How much is health coverage going to cost you? What about short and long term disability? Paid time off? Holidays? All of these things are super important. Make sure you don't let the salary number blind you.