Páginas

SyntaxHighlighter

terça-feira, 8 de novembro de 2011

Using google-oauth-java-client to consume a Google App Engine OAuth protected resource

This post shows how to write a java based OAuth client to make requests against a Google App Engine OAuth protected resource using the google-oauth-java-client.

If you want to know how to create an OAuth provider or how to register your domain and get your consumer key and secret, I highly recommend you to read this great blog post written by Ikai Lan.
Actually, you should read it anyway, because the piece of code below just replaces the python script provided by him. Since I needed a java version I decided to write my own client.

The java class below contains a basic junit test which will do the 3-legged OAuth dance. The only thing I haven't implemented is the access token cache part - every time you run this test you will have to explicitly perform the authorization steps in the browser (again).

In  order to run it, you basically have to resolve 2 dependencies:
I used the 1.6.0-beta version. Just download and put them in the classpath and you are good to go!

And, of course, don't forget to change the APP_ID and CONSUMER_SECRET constants.

package com.ciandt.oauth.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.junit.Test;

import com.google.api.client.auth.oauth.OAuthAuthorizeTemporaryTokenUrl;
import com.google.api.client.auth.oauth.OAuthCredentialsResponse;
import com.google.api.client.auth.oauth.OAuthGetAccessToken;
import com.google.api.client.auth.oauth.OAuthGetTemporaryToken;
import com.google.api.client.auth.oauth.OAuthHmacSigner;
import com.google.api.client.auth.oauth.OAuthParameters;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;

public class OAuthClientTest {

 private static final HttpTransport TRANSPORT = new NetHttpTransport();
 
 private static final String APP_ID = "your_app_id_here";
 private static final String CONSUMER_KEY = APP_ID + ".appspot.com";
 private static final String CONSUMER_SECRET = "your_consumer_secret_here";
 
 private static final String PROTECTED_SERVICE_URL = "https://" + APP_ID + ".appspot.com/resource";
 private static final String REQUEST_TOKEN_URL = "https://" + APP_ID + ".appspot.com/_ah/OAuthGetRequestToken";
 private static final String AUTHORIZE_URL = "https://" + APP_ID + ".appspot.com/_ah/OAuthAuthorizeToken";
 private static final String ACCESS_TOKEN_URL = "https://" + APP_ID + ".appspot.com/_ah/OAuthGetAccessToken";

 @Test
 public void consumeProtectedResource() throws Throwable {

  // this signer will be used to sign all the requests in the "oauth dance"
  OAuthHmacSigner signer = new OAuthHmacSigner();
  signer.clientSharedSecret = CONSUMER_SECRET;

  // Step 1: Get a request token. This is a temporary token that is used for 
  // having the user authorize an access token and to sign the request to obtain 
  // said access token.
  OAuthGetTemporaryToken requestToken = new OAuthGetTemporaryToken(REQUEST_TOKEN_URL);
  requestToken.consumerKey = CONSUMER_KEY;
  requestToken.transport = TRANSPORT;
  requestToken.signer = signer;

  OAuthCredentialsResponse requestTokenResponse = requestToken.execute();
  
  System.out.println("Request Token:");
  System.out.println("    - oauth_token        = " + requestTokenResponse.token);
  System.out.println("    - oauth_token_secret = " + requestTokenResponse.tokenSecret);

  // updates signer's token shared secret
  signer.tokenSharedSecret = requestTokenResponse.tokenSecret;

  OAuthAuthorizeTemporaryTokenUrl authorizeUrl = new OAuthAuthorizeTemporaryTokenUrl(AUTHORIZE_URL);
  authorizeUrl.temporaryToken = requestTokenResponse.token;
  
  // After the user has granted access to you, the consumer, the provider will
  // redirect you to whatever URL you have told them to redirect to. You can 
  // usually define this in the oauth_callback argument as well.
  String currentLine = "n";
  System.out.println("Go to the following link in your browser:\n"
    + authorizeUrl.build());
  InputStreamReader converter = new InputStreamReader(System.in);
  BufferedReader in = new BufferedReader(converter);
  while (currentLine.equalsIgnoreCase("n")) {
   System.out.println("Have you authorized me? (y/n)");
   currentLine = in.readLine();
  }
  
  // Step 3: Once the consumer has redirected the user back to the oauth_callback
  // URL you can request the access token the user has approved. You use the 
  // request token to sign this request. After this is done you throw away the
  // request token and use the access token returned. You should store this 
  // access token somewhere safe, like a database, for future use.
  OAuthGetAccessToken accessToken = new OAuthGetAccessToken(
    ACCESS_TOKEN_URL);
  accessToken.consumerKey = CONSUMER_KEY;
  accessToken.signer = signer;
  accessToken.transport = TRANSPORT;
  accessToken.temporaryToken = requestTokenResponse.token;

  OAuthCredentialsResponse accessTokenResponse = accessToken.execute();
  System.out.println("Access Token:");
  System.out.println("    - oauth_token        = " + accessTokenResponse.token);
  System.out.println("    - oauth_token_secret = " + accessTokenResponse.tokenSecret);
  System.out.println("\nYou may now access protected resources using the access tokens above.");

  // updates signer's token shared secret
  signer.tokenSharedSecret = accessTokenResponse.tokenSecret;

  OAuthParameters parameters = new OAuthParameters();
  parameters.consumerKey = CONSUMER_KEY;
  parameters.token = accessTokenResponse.token;
  parameters.signer = signer;

  // utilize accessToken to access protected resources
  HttpRequestFactory factory = TRANSPORT.createRequestFactory(parameters);
  GenericUrl url = new GenericUrl(PROTECTED_SERVICE_URL);
  HttpRequest req = factory.buildGetRequest(url);
  HttpResponse resp = req.execute();
  System.out.println("Response Status Code: " + resp.getStatusCode());
  System.out.println("Response body:" + resp.parseAsString());

 }

}

terça-feira, 11 de outubro de 2011

Automating GAE's application deployment with Jenkins

Follow below the expect script I created to automate the deployment of applications on GAE. It simply invokes/spawns the appcfg.sh and sends the account's password when prompted.
Then I configured a Jenkins job which contains a "execute shell" step invoking this script.

Jenkins "execute shell" step

expect $WORKSPACE/appcfg.exp "$APPENGINE_HOME" "myuser@gmail.com" "mypassword" "update" "$WORKSPACE/war"

appcfg.exp expect script

#!/usr/bin/expect -f
# Expect script to supply GAE's account password for appcfg.sh
#
# This script needs four arguments:
# username = GAE's google account email
# password = GAE's google account password
# warDir = war directory to deploy to GAE
# gaeHome = GAE's SDK home dir
#
# For example:
#  expect appcfg.exp myemail@gmail.com mypassword ./war /usr/share/appengine-sdk-1.5.3

if {[llength $argv] == 0} {
   puts "usage: appcfg.exp {-index|#}"
   exit 1
}

set gaeHome [lrange $argv 0 0]
set username [lrange $argv 1 1]
set password [lrange $argv 2 2]
set cmd [lrange $argv 3 3]
set warDir [lrange $argv 4 4]

set timeout -1

# spawns appcfg.sh
spawn $gaeHome/bin/appcfg.sh --enable_jar_splitting --passin --email=$username $cmd $warDir
match_max 100000

expect {
   default {exit 0}
   # Look for passwod prompt
   "*?assword*"
}

# Send password aka $password
send -- "$password\r"

# send blank line (\r) to make sure we get back to gui
send -- "\r"
expect eof

Using XmlSlurper to update appengine-web.xml

I'm currently working on a project which is using GAE (Google Application Engine).
I wanted to setup Jenkins' jobs to update the different stages (acceptance, qa, uat) in our build pipeline.
To achieve that I created 3 different GAE's applications - one for each stage - and 4 Jenkins jobs: 1 template which holds the common steps and 3 others for the respective stages.
The first step of the template job updates appengine-web.xml target application and version based on parameters passed to the job.


See the groovy script below:


import groovy.xml.StreamingMarkupBuilder

//getting parameters values from environment variables
def env = System.getenv()
def workspace = env["WORKSPACE"]
def applicationName = env["TARGET_APPLICATION_NAME"]
def versionName = env["TARGET_VERSION_NAME"]
def ant = new AntBuilder()
ant.echo(message:"Opening $workspace/war/WEB-INF/appengine-web.xml")
def file = new File("$workspace/war/WEB-INF/appengine-web.xml")
def root = new XmlSlurper().parse(file)
ant.echo(message:"Updating appengine-web.xml with application: $applicationName and version: $versionName")
root.application=applicationName
root.version=versionName 
def outputBuilder = new StreamingMarkupBuilder()
String result = outputBuilder.bind{ 
   mkp.declareNamespace("":  "http://appengine.google.com/ns/1.0")    
   mkp.yield root 
}
ant.echo(message:"Writing appengine-web.xml")
file.write(result)

quinta-feira, 31 de março de 2011

JMeter - Processing and handling JSON responses using BSF post processors

In one of my Jmeter posts I talked about how to post JSON data using a HttpSampler.
Now I want to show some cool stuff we can do using Jmeter's javascript/BSF support for handling JSON responses.

Suppose we have the following JSON response:

{ success: true, data:[ 1,2,3,4,5 ]  }

This string can be easily converted to a javascript object by using the eval function like this:

eval( 'var myObj = ' + prev.getResponseDataAsString() )

After the evaluation we can use the variable "myObj" as we wish and do things like accessing the success property:

log.info(myObj.success)

Or iterate over the list in the data property:

for (  var i = 0;  i < myObj.data.length; i++ ) {
     log.info(myObj.data[i])
}

We can also put the object in the implicit vars object so it can be used in another sampler, for example:

vars.putObject('myObj', myObj)

sexta-feira, 25 de março de 2011

Generating CSV files using sqlcmd and groovy

Just sharing a simple groovy script I wrote for generating a CSV file from the database.
It depends on SQLServer's sqlcmd command line utility.
It takes 4 input parameters - needed to open a database connection, scans the current directory for .sql files then executes each of them using sqlcmd generating a .temp file. The temp file is then processed - the first and last 2 lines are removed - and renamed to a .csv file.

def username = args[0]
def password = args[1]
def host = args[2]
def database = args[3]
def dir = './'

def ant = new AntBuilder()
def p = ~/.*\.sql/
new File( dir ).eachFileMatch(p) { f ->

    def sqlFile = f.name
    def tempCsvFile = sqlFile.replaceAll(/.sql/,'')

    def cmd = "sqlcmd -S $host -U $username -P $password -d ${database} -i ${sqlFile} -W  -o ${tempCsvFile}.temp -s ;"
    def process = cmd.execute()
    process.waitFor()

    ant.move(file: "${tempCsvFile}.temp", tofile:"${tempCsvFile}.csv", overwrite: true ) {
 filterchain(){
  headfilter(lines:"-1", skip: "2")
  tailfilter(lines: "-1", skip: "2" )
  ignoreblank()
 }
    }
}

This could probably be achieved in many other ways.
But it worked like a charm for me!!

terça-feira, 22 de março de 2011

JMeter - POSTing / Sending JSON data

Recently I was load testing a RESTful mobile API where all the transmitted data were encoded as JSON. It took me some time to figure out how to simulate that in JMeter using a regular HTTPRequest sampler so I decided to share it with others.

NOTE: I successfuly used this technique for webservices exposed by the Jersey library as well as ASP.NET asmx webservices

It's quite simple. Assuming you need to send POST request to the /login URI passing two parameters: email and password encoded as JSON, like this:

{ email: 'myemail@email.com', password: 'mypassword' }

Just follow two basic steps:

Step 1 - Setting the "ContentType" http header attribute to "application/json"
  • Add an HTTP Header Manager to your test plan;
  • Add a new attribute: set name to "Content-Type" and value to "application/json"



Step 2 - Setting the JSON object as an unnamed http request parameter
  • Add a HttpRequest sampler
  • Add a new parameter, leave the name blank and set the value to the JSON string:  { 'email': 'myemail@email.com', 'password': 'mypassword' }

And last but not least: you can use variables and properties as you wish in the JSON string. Suppose you have a CSV Data Set Config which defines to external variables: email e pwd. You could use them in your sampler like this:

{ email: '${email}', password: '${pwd}' }

terça-feira, 9 de novembro de 2010

A arte de desenvolver sem branches

Ultimamente eu e meu time estamos evitando ao máximo a criação de branches no SVN. Nosso objetivo é comitar apenas no trunk e tentar manter a aplicação sempre releaseable. Acreditamos que isso ajuda a manter o fluxo de entregas constante e o caminho aberto para o sonhado continuous deployment e delivery.

Nossas últimas experiências com feature branch foram desagradáveis. Além dos (muito) chatos, às vezes longos, e arriscados merges envolvidos na criação de um novo branch, o que mais incomoda é a falta de integração contínua e o "tumulto" causado quando o branch é reintegrado ao trunk - dependendo do tamanho e o grau de intrusão é necessária uma longa fase de estabilização.

Para evitar esse transtorno, uma prática que começamos a adotar é, ao invés de criar branches no controlador de versão, fazer as ramificações no próprio código fonte - branch by abstraction. Basicamente o que fazemos é criar flags em arquivos de propriedades da aplicação e utilizá-las para esconder/desabilitar uma feature incompleta (GUI) ou para chavear entre uma implementação ou outra.

Características do design, por exemplo utilização de patterns como  Dependency Injection, Strategy e Factory, facilitam bastante o trabalho. Mais que isso, já vi casos de refactorings para suportar esse tipo de chaveamento, melhorar o design através da redução do acoplamento, e nascimento de novos componentes!

É uma estratégia bem simples de adotar e que traz alguns benefícios. Como continuamos a comitar no mainline, o código segue sendo continuamente integrado pelo Hudson juntamente com as demais features. Se bem planejado e estruturado, é possível entregar a feature aos poucos, em pequenas partes, reduzindo o risco das implantações. A cada release pouquíssimo código novo é implantado e consequentemente a identificação de eventuais bugs ou efeitos colaterais indesejados é antecipada, reduzindo o custo de investigação e correção.

Para ilustrar: suponha que temos uma nova demanda que alterará drasticamente o cerne da aplicação. Pior que isso, a estimativa para terminá-la é de alguns dias, podendo chegar até uma semana. Em paralelo várias outras melhorias e correções de bugs menores irão acontecer. O objetivo é entregar o quanto antes tudo o que for ficando pronto e evitar ao máximo o "represamento" de código.

Enxergo três opções: A primeira, e pior de todas, é sair implementando tudo no trunk deixando a aplicação unreleaseable por um longo período de tempo. A segunda é criar uma feature branch para a alteração maior e desenvolver as demandas menores no trunk, dessa forma conseguimos manter o ritmo de entregas, mas o código da alteração maior é "estocado". A última é criar um code flip, implementar todas as demandas no trunk só que deixando a grande alteração dormente e escondida. Apesar de incompleto e invisível para o usuário o código faz parte dos binários entregues, passou pela integração contínua e por QA - já está sendo aceito, está implantado!

Alguns pontos de atenção caso opte pela feature flag:
  • Aposentar os code flips e garantir a remoção do código obsoleto;
  • Manter ambos os caminhos (code paths) enquanto as duas versões coexistirem.
Volto a mencionar que esse tipo de decisão precisa ser bem pensada. Já tivemos períodos tensos com bloqueamento de implantações - tanto com branches normais como abstratas. Se seu cliente estiver "mal acostumado" com várias entregas por semana, por exemplo, e você começar a faze-lás quinzenalmente, pode ter certeza que ele vai reclamar. Além do que, a sensação de receber software concreto quase que diariamente, é mil vezes melhor que um "burndown na pinta".

É importante ressaltar que apesar do título, não estou dizendo que nunca criarei branches ou que é sempre melhor evitá-los. Sei que existem casos onde não há outra saída. O importante é saber tomar esse tipo de decisão - focar nas entregas mais frequentes.

Para se aprofundar melhor: