Páginas

SyntaxHighlighter

terça-feira, 17 de junho de 2014

Pig Design Patterns: free book giveaway

Book giveaway
Hold a chance to win free copy of Pig Design Patterns, just by commenting and sharing! For the contest we have 3 e-copies of Pig Design Patterns, to be given away to 3 lucky winners.

How you can win
To win your copy of this book, all you need to do is come up with a comment below highlighting the reason “why you would like to win this book” and share a post about this book with this unique short link on your social media profiles: bit.ly/1iuZCUL

Don’t forget to drop your email address and the permalink of your social media post in your comment.

Note: Social Media post must contain this unique short link: bit.ly/1iuZCUL

Duration of the contest & selection of winners:
The contest is valid till 30th June 2014, and is open to everyone. Winners will be selected on the basis of their comment posted.



quarta-feira, 5 de março de 2014

Hooking Bitbucket up with Jenkins parameterized jobs

Bitbucket repositories allow us to setup hooks which notify/trigger Jenkins' jobs about newly pushed code. The process to create such a hook is documented here. However, it doesn't mention how to integrate with Jenkins parameterized jobs.
After reading the Jenkins documentation and a few trial and error I managed integrate Bitbucket and Jenkins parameterized jobs.
The hook management form presents 4 fields:

  1. Endpoint: Here, you’ll need to set your Jenkins URL in the following format — http://username:apitoken@your.jenkins.url/job/your.job.name/buildWithParameters 
  2. Module name: (Optional)
  3. Project name: (Leave Empty)
  4. Token: It’s the authentication token you defined in your Jenkins job settings 

The gotcha is leaving the project name field blank and include it in the endpoint URL appended with buildWithParameters.


sábado, 27 de julho de 2013

Dynamically generating Zip files using Google Cloud Storage Client Library for Appengine

Let's say you have 50 objects (15Mb each) stored in Google Cloud Storage. Now, you need to create a zip archive containing all of them and store the resultant file back into GCS. How can we achieve that from within an Appengine java application?
Well, after some research, I wrote the method below using Google Cloud Storage Client Library which does exactly that. Just don't forget  to grant the appropriate permissions to your appengine service account so that it can read and write the objects.


public static void zipFiles(final GcsFilename targetZipFile,
  final GcsFilename... filesToZip) throws IOException {

 Preconditions.checkArgument(targetZipFile != null);
 Preconditions.checkArgument(filesToZip != null);
 Preconditions.checkArgument(filesToZip.length > 0);

 final int fetchSize = 4 * 1024 * 1024;
 final int readSize = 2 * 1024 * 1024;
 GcsOutputChannel outputChannel = null;
 ZipOutputStream zip = null;
 try {
  final GcsFileOptions options = new GcsFileOptions.Builder()
    .mimeType(MediaType.ZIP.toString()).build();
  outputChannel = GCS_SERVICE.createOrReplace(targetZipFile, options);
  zip = new ZipOutputStream(Channels.newOutputStream(outputChannel));
  GcsInputChannel readChannel = null;
  for (final GcsFilename file : filesToZip) {
   try {
    final GcsFileMetadata meta = GCS_SERVICE.getMetadata(file);
    if (meta == null) {
     LOGGER.warning(file.toString()
       + " NOT FOUND. Skipping.");
     continue;
    }
    final ZipEntry entry = new ZipEntry(file.getObjectName());
    zip.putNextEntry(entry);
    readChannel = GCS_SERVICE.openPrefetchingReadChannel(file,
      0, fetchSize);
    final ByteBuffer buffer = ByteBuffer.allocate(readSize);
    int bytesRead = 0;
    while (bytesRead >= 0) {
     bytesRead = readChannel.read(buffer);
     buffer.flip();
     zip.write(buffer.array(), buffer.position(),
       buffer.limit());
     buffer.rewind();
     buffer.limit(buffer.capacity());
    }

   } finally {
    zip.closeEntry();
    readChannel.close();
   }
  }
 } finally {
  zip.flush();
  zip.close();
  outputChannel.close();
 }
}

sábado, 6 de julho de 2013

"Smart" Appengine Devserver restarts for faster development lifecycle

We just started a new AppEngine Java project using the appengine-maven-plugin and the Eclipse IDE (Juno). We are using the appengine:devserver goal to start the devserver. It basically builds the entire project (compile, test and package) and after that launches the devserver pointing it to the generated webapp directory - which by default is: ${project.build.directory}/${project.build.finalName}


The Problem

Every edit made in the source directory is not recognized unless the server is restarted - neither static content nor newly compiled classes. Which is obvious since the devserver is monitoring only the target directory.
It's a very "bureaucratic" and nonproductive development environment - we need to stop and start the server even for a single CSS line change.


The Dream

Achieve the same productivity level we have when working with dynamic languages based development environment (i.e. Python). Just hit F5 in the browser to see the changes in static files and automatic server reload every time a Java class or descriptor file is compiled/changed.


The Solution 

Using a little Ant-foo, we were able to create a target which synchronizes both directories.
Thie snippet below uses the sync Ant task to perform the static content synchronization (lines 1-9). Notice that everything inside src.webapp.dir is sync'ed except for the 3 directories declared in the preserveintarget element. We had to exclude them from the synchronization process because since they only exist in the target directory they'd be deleted otherwise. And finally a second sync to synchronize the compiled classes (lines 11-13).

<sync verbose="true" todir="${target.webapp.dir}" includeEmptyDirs="true">
 <fileset dir="${src.webapp.dir}" />
 <preserveintarget>
     <!-- Ignore the directories below -->
  <include name="WEB-INF/lib/**" />
  <include name="WEB-INF/classes/**" />
  <include name="WEB-INF/appengine-generated/**" />
 </preserveintarget>
</sync>

<sync verbose="true" todir="${target.webapp.dir}/WEB-INF/classes">
 <fileset dir="${basedir}/target/classes" />
</sync>

Then, we attached it to an Eclipse builder, which is triggered every time a change is made in the project ("Build automatically" flag enabled).
The same behavior can be achieved by creating a special maven profile and using a combination of the m2e lifecycle mappings and the maven antrun plugin.
Something like this:

<profile>
    <id>m2e</id>
    <activation>
        <property>
            <name>m2e.version</name>
        </property>
    </activation>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.7</version>
                <executions>
                    <execution>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <property name="target.webapp.dir" value="${project.build.directory}/${project.build.finalName}" />
                                <property name="src.webapp.dir" value="${basedir}/src/main/webapp" />
                                <sync verbose="true" todir="${target.webapp.dir}" includeEmptyDirs="true">
                                    <fileset dir="${src.webapp.dir}" />
                                    <preserveintarget>
                                        <include name="WEB-INF/lib/**" />
                                        <include name="WEB-INF/classes/**" />
                                        <include name="WEB-INF/appengine-generated/**" />
                                    </preserveintarget>
                                </sync>
                                <sync verbose="true" todir="${target.webapp.dir}/WEB-INF/classes">
                                    <fileset dir="${basedir}/target/classes" />
                                </sync>
                            </target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <!-- This plugin's configuration is used to store Eclipse m2e settings 
      only. It has no influence on the Maven build itself. -->
                <plugin>
                    <groupId>org.eclipse.m2e</groupId>
                    <artifactId>lifecycle-mapping</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <lifecycleMappingMetadata>
                            <pluginExecutions>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>org.apache.maven.plugins</groupId>
                                        <artifactId>maven-antrun-plugin</artifactId>
                                        <versionRange>[1.6,)</versionRange>
                                        <goals>
                                            <goal>run</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <execute>
                                            <runOnIncremental>true</runOnIncremental>
                                        </execute>
                                    </action>
                                </pluginExecution>
                            </pluginExecutions>
                        </lifecycleMappingMetadata>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</profile>

quarta-feira, 6 de março de 2013

How to reference the current jmeter script base path?

I use lots of javascript in my Jmeter's test plans. I usually keep the code in the "Script" text area, either in BSF or JSR223 assertions and/or processors.
Sometimes however, I'd rather keep the scripts in a separate (external) file to ease maintenance.
Differently from the "CSV Data Set Config" element, the "JSR223 Assertion" "Script File" property does not use the current running script directory as the base path, it uses the user.dir system property instead.

The problem is that I normally organize my test assets following this directory layout:

/my_project/my_test.jmx       $jmx files
/my_project/js/script.js      $script files
/my_project/data/my_test.csv  $csv files

In order to reference scripts with paths relative to the current JMX file I use the FileServer class, in conjunction with the __javaScript function, like this:

${__javaScript(org.apache.jmeter.services.FileServer.getFileServer().getBaseDir())}/js/script.js

sábado, 10 de novembro de 2012

Expondo uma API para busca de CEPs com Google Cloud Endpoints

No Google IO 2012 foi lançado (por enquanto só para trusted testers) o Google Cloud Endpoints.
É um novo serviço do GAE que facilita (e muito) a publicação de APIs RESTful ou JSON RPC.
Na verdade as facilidades vão muito além do servidor. Foi incorporado no GPE (Google Plugin para Eclipse) um "gerador" que dada uma API, gera o código necessário para acessá-la de clientes: Android (Java) e/ou iOS (objective C). Além disso também é possível acessar os serviços via javascript usando o Google APIs Client Library for Javascript (mesma biblioteca utilizada para utilização das APIs Google).


Para testar esse novo serviço, me inscrevi no programa de trusted testers e criei uma aplicação que expõe uma API REST para busca de CEPs - usando uma biblioteca Java para busca de CEPs que criei um tempo atrás.
A aplicação possui apenas uma única classe: CepEndpoint. O gist abaixo mostra como o código é simples e como algumas simples anotações são suficientes para publicar um endpoint composto por alguns serviços.
Loading ....

Para testar a API publicada pode-se usar o Google APIs Explorer ou o "clientzinho web" que criei que invoca esse mesmo endpoint usando a API javascript.
Loading ....

Se derem uma olhada no código fonte, verão que aproveitei também pra dar uma treinada no desenvolvimento de aplicações HTML5 usando Angular JS e Bootstrap.

Use o link abaixo para acessar a aplicação:
http://busca-cep.appspot.com/

Setting up Jenkins on EC2 using AWS CloudFormation (including nginx as a reverse proxy)

I lost count on how many times I had to setup a CI server. Being a big fan of the concept of "Infrastructure as Code" as I am, I promised myself that the next time I'd do it differently (I needed to have it automated!).

Well, the day has come.The stack (using Cloudformation terms) I decided to create is composed by a basic Jenkins installation (standalone + winstone) running as a daemon on an Amazon Linux based EC2 intance. It also includes Nginx as a reverse proxy and a dedicated volume for storing the JENKINS_HOME files and other artifacts.

Obviously that a much easier, simpler and (I think) even cheaper alternative would be using some PaaS offering (Jenkins as a Service).

However, I ended up using AWS Cloudformation to automate the provisioning of the AWS Resources (IAM User, SecurityGroup, EBS Volumes, EC2 instance) and its cloud-init features to install and configure the necessary packages.

External repository addition, yum based package installations, configuration files adjustments, EBS volumes setup, etc. Most of the installation and configuration logic is in a shell script embedded in the UserData property.

The resulting template is right below, the input parameters are: Instance type, JENKINS_HOME volume size in gigabytes and the EC2 KeyName. The output is the Jenkins server URL!

Loading ....


sexta-feira, 6 de julho de 2012

API Java para busca de CEPs (fachada para o serviços dos Correios)

O site dos Correios disponibiliza gratuitamente um ótimo serviço para busca online de CEPs.
Porém, essa é a única forma de acessá-lo: via página HTML.
Não existe, nem é exposta, nenhuma API que facilite a integração desse serviço com outras aplicações.

Para possibilitar esse tipo de integração, criei o busca-cep-java-client que nada mais é que um componente Java (jar) cuja API abstrai a complexidade de:
  1. Fazer a requisição HTTP (GET) passando os parâmetros necessários e;
  2. Processar a resposta - extraindo os dados de CEP do HTML retornado. 
Internamente utilizo a biblioteca HtmlUnit para auxiliar no Web Scraping. Basicamente o que eu faço é simular um usuário que entra na página de busca, preenche e posta o form. Depois, já na tela de resultados, percorro o HTML retornado em busca da tabela com os dados de CEP retornados.

O trecho de código abaixo mostra o quão simples é a utilização da API:

// Obtém uma instância de CEPService
CEPService buscaCEP = CEPServiceFactory.getCEPService();
// Obtém um CEP pelo número
CEP cep = buscaCEP.obtemPorNumeroCEP(13084440);
// Obtém todos os CEPs que contém "Flordalisa" no logradouro
List<CEP> ceps = buscaCEP.obtemPorEndereco("Flordalisa");

Para mais detalhes em como utilizar esse componente, sugiro dar uma olhada nos testes unitários.
Todo o código fonte também está disponível no Github.
Além disso, publiquei a versão 1.1 do componente num repositório Maven (também no Github).
Para quem usa essa ferramenta basta alterar o pom.xml para incluir o repositório:

<repository>
        <id>Talesolutions</id>
        <url>https://raw.github.com/fabito/talesolutions-mvn-repo/master/</url>
        <snapshots>
                <enabled>true</enabled>
        </snapshots>
        <releases>
                <enabled>true</enabled>
        </releases>
</repository>

E adicionar essa dependência:

    <dependency>
      <groupId>org.talesolutions</groupId>
      <artifactId>busca-cep-client</artifactId>
      <version>1.1</version>
    </dependency>

Considerações Finais

O busca-cep-client ajuda a integrar funcionalidades de busca de CEPs à aplicações Java. Principalmente porque dispensa a criação de uma base de CEPs local e como é só uma façade para o serviço oficial dos Correios temos a "garantia" de estarmos sempre acessando dados atuais e corretos.
A principal desvantagem é a dependência da disponibilidade e formato desse serviço - alto acoplamento. Se o site dos Correios sair do ar sua aplicação certamente será impactada. Além disso, como os dados de CEPs são extraidos de uma página HTML, qualquer mudança no markup gerado afetará o funcionamento do componente.

quinta-feira, 12 de abril de 2012

JMeter - Using BSF Assertion to fail samples based on JSON response


I've tested some web applications whose unexpected errors or exceptions were handled by the application code and encoded in the response body as JSON.

A hypothetical response is shown below:

{
    data: null,
    message: "TimeoutException: Transaction rolled back after 3000 seconds.",
    success: false
}

The problem with this approach is that most of the times the HTTP response code is 200 (OK) which  jmeter interprets as a successful sample.
I'm not a big fan of this solution I'd rather have it returning 4XX or 5XX response codes, in which case jmeter would fail the sample.

In order to workaround this, I normally use a BSF Assertion containing javascript code similar to:

try {
   eval('var response = ' + prev.getResponseDataAsString());
   if (!response.success) {
      prev.setSuccessful(false);
      prev.setResponseMessage(response.message);
   }
} catch(e) {
      prev.setSuccessful(false);
      prev.setResponseMessage("Invalid response. Expected a valid JSON.");
}

First I try to evaluate the response string into a valid javascript object. If that succeeds I check the success flag and handle it properly, otherwise, in the catch block, I force the sample to fail due to a bad response format.
Note that in both failure scenarios I invoke the setSuccessful and the setResponseMessage methods to signal a sample failure and have better detailed error messages respectively.

And that's it!
Happy load testing!

JMeter - Changing sample label/name at runtime

When creating jmeter's test plans, sometimes its useful to change the samples names in order to have more accurate and meaningful reports. To achieve this I normally use one of these two approaches:

Using variables (placeholders) in the sampler name

If the variables you need to form the new sample name are already available just fill the the name's text field using the syntax ${variable name}.
The only problem with this approach is that the name shown in test plan tree (left panel) might end up being not so intuitive.


Dynamically setting it in BSF Post Processors

Just use the variable prev (which gives access to the previous SampleResult) and use the method setSampleLabel to set the new label.
In the example below I'm using the value of another variable (named url) previously put in the vars object:

quinta-feira, 9 de fevereiro de 2012

Instalando a slackline (sem árvores) com auxílio de âncora - Parte 2

Comecei a execução do meu projeto.
Abaixo algumas fotos do sistema de ancoragem que montei no meu quintal para instalar minha slackline.

1) Escavação



2) Preparando da âncora



3) Enterrando a âncora


   


4) Finalmente, armando a slack


Slackline / Waterline in the Colombian Caribbean

Slackline and waterline sessions during my last trip to the Colombian Caribbean. 
This video includes waterlining in the Lover's bridge (between Providencia and Santa Catalina Island) and slacklining in Johnny Cay - in San Andres Island.



sexta-feira, 30 de dezembro de 2011

Instalando a slackline (sem árvores) com auxílio de âncora - Parte 1


No meu quintal não existem árvores nem outra estrutura para instalar minha slackline.
Pesquisando um pouco descobri o conceito de dead men anchor que nada mais é que uma âncora, no meu caso de madeira, enterrada a 1,5m de profundidade.
Porém, diferentemente desses vídeos, eu não quero enterrar a slackline. Quero uma solução permanente que permita instalar e desinstalar a fita quando quiser.

Por isso resolvi usar cordas de nylon amarradas à âncora. A ideia é amarrar a slackline nessas cordas e usar algum outro tipo de estrutura bem firme: a-frame, cavalete, tronco ou caixa para erguê-la.

Por enquanto, é só um plano. Segue meus esboços:



Assim que começar o projeto posto por aqui!

sábado, 3 de dezembro de 2011

Importing data from Oracle to Google App Engine Datastore with a custom bulkloader connector

I'm working on a project where we need to upload data from Oracle databases to Datastore very frequently.
The datastore bulkloader  bundled in the App Engine Python SDK works very well for data importing and exporting. It currently offers 3 different connector implementations: csv, xml and simpletext.
CSV connector was working smoothly for us but I wanted to avoid the CSV file generation. I wanted to bulkload directly from Oracle.
After some study I decided to write a new connector: the oracle_connector.
The only pre requisite for using this connector is having the cx_Oracle python module properly installed.
Here are some details about my environment:
  • Ubuntu 11.10 - 64 bits
  • Python 2.7.2+
  • cx_Oracle 5.0.4 unicode
  • Oracle Instant Client 11.2
  • Google App Engine Python SDK 1.6.0
Some design decisions

I wanted to specify a sql query for every kind/entity in the yaml config file and then map the returned (selected) column names or aliases to the properties via the external_name attribute. I didn't find a way to use a custom connector_options like "sql_query" so I used the existent columns option to inform the query.
I also wanted to have the database connection properties to be outside the connector implementation. In order to achieve that I decided to store these configurations in an external file which is passed through the command line "--filename" parameter to the appcfg.py script.

The actual implementation

It is basically comprised of 3 files:
  • bulkloader.yaml - imports and utilizes the oracle_connector as well as maps queries to entities
  • oracle_connector.py - connector implementation based on cx_Oracle (doesn't work for exports)
  • db_settings.py - defines connection properties variables used by oracle_connector
Invoking the bulkloader is very simple:

$APPENGINE_HOME/appcfg.py upload_data --config_file=bulkloader.yaml --kind=Table --filename=db_settings.py --email=user@gmail.com --url=http://app-id.appspot.com/remote_api

The bulkloader.yaml below shows how to import and use the oracle_connector. Also note the connector_options attribute.

python_preamble:
- import: base64
- import: re
- import: oracle_connector
- import: google.appengine.ext.bulkload.transform
- import: google.appengine.ext.bulkload.bulkloader_wizard
- import: google.appengine.ext.db
- import: google.appengine.api.datastore
- import: google.appengine.api.users

transformers:

- kind: Table
  connector: oracle_connector.OracleConnector.create_from_options
  connector_options:
    columns: "select TABLE_NAME, TABLESPACE_NAME, LAST_ANALYZED from user_tables"
  property_map:
    - property: __key__
      external_name: TABLE_NAME

    - property: tablespace
      external_name: TABLESPACE_NAME

    - property: last_analyzed
      external_name: LAST_ANALYZED

There are some known issues here: 1) you must use uppercase strings in the external_name attribute and 2) I wasn't able to return number columns (had to use to_char oracle function) due to some problem in my cx_Oracle installation.

Below, the OracleConnector class which implements the connector_interface.ConnectorInterface. This was my first piece of python code. Let me know if I can improve it!

#!/usr/bin/env python
"""A bulkloader connector to read data from Oracle selects.
"""
from google.appengine.ext.bulkload import connector_interface
from google.appengine.ext.bulkload import bulkloader_errors
import cx_Oracle
import os.path

class OracleConnector(connector_interface.ConnectorInterface):

  @classmethod
  def create_from_options(cls, options, name):
    """Factory using an options dictionary.

    Args:
      options: Dictionary of options:
        columns: sql query to perform, each selected column becomes a column
      name: The name of this transformer, for use in error messages.

    Returns:
      OracleConnector object described by the specified options.

    Raises:
      InvalidConfiguration: If the config is invalid.
    """
    columns = options.get('columns', None)
    if not columns:
        raise bulkloader_errors.InvalidConfiguration(
            'Sql query must be specified in the columns '
            'configuration option. (In transformer name %s.)' % name)

    return cls(columns)

  def __init__(self, sql_query):
    """Initializer.

    Args:
      sql_query: (required) select query which will be sent to database. The returned columns/aliases will be used as the connectors column names
    """
    self.sql_query = unicode(sql_query)

  def generate_import_record(self, filename, bulkload_state):
    """Generator, yields dicts for nodes found as described in the options.

    Args:
      filename: py script containing oracle database connection properties: host, port, uid, pwd and service.
      bulkload_state: Passed bulkload_state.

    Yields:
      Neutral dict, one per row returned by the sql query
    """
    dbprops = __import__(os.path.splitext(filename)[0])
    dsn_tns = cx_Oracle.makedsn(dbprops.host, dbprops.port, dbprops.service)
    connection = cx_Oracle.connect(dbprops.uid, dbprops.pwd, dsn_tns)
    cursor = connection.cursor()
    cursor.arraysize = dbprops.cursor_arraysize
    cursor.execute(self.sql_query)
    num_fields = len(cursor.description)
    field_names = [i[0] for i in cursor.description]
    for row in cursor.fetchall():
       decoded_dict = {}
       for i in range(num_fields):
         decoded_dict[field_names[i]] = row[i]
       yield decoded_dict    
    cursor.close()
    connection.close()

And finally the contents of db_settings.py:

uid=u'database_username'
pwd=u'database_password'
host="database_host"
port=1521
service="service"
cursor_arraysize = 50

Get the code

You can download the code here:

https://github.com/fabito/gae_bulkloader_connectors

Summary

This post shows an alternative connector implementation for importing data directly from an Oracle database.
This approach could be easily extended to support other RDBMS such as MySQL or Postgres.
We still have to perform some tests to check how it will behave under different loads and data types but so far, it seems promising.

References

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!!