segunda-feira, 7 de dezembro de 2009

PostgreSQL monitoring on ZABBIX



I've recently started to get to know ZABBIX [1] a little deeper, especially regarding PostgreSQL database servers monitoring. At first sight I thought it an incredible monitoring and notification system as it fulfills most of the requirements I wondered to have in Cedrus [2].

After setting up some basic OS-related items to be monitored, I was searching for PostgreSQL specific configurations and then I found a wiki [3] on ZABBIX UserParameters. It is indeed very simple! You just need to create SQL statements and then invoke them with psql. If successful, you could append the corresponding lines into ZABBIX agent configuration file and restart it. Then, in the front-end application, what is left to do is to properly set these PostgreSQL parameters to a given host.

I've created some additional parameters I always use in PostgreSQL instances in order to monitor the server health. In this case, I previously created a user called "zabbix" and a database with same name on PostgreSQL.

Here are the included lines on /etc/zabbix/zabbix_agentd.conf:


# PostgreSQL custom parameters

# instance version
UserParameter=pgsql.version,psql -U zabbix zabbix -Atc 'select version()'

# instance databases summary
UserParameter=pgsql.db.summary,psql -c "select a.datname, pg_size_pretty(pg_database_size(a.datid)) as size, cast(blks_hit/(blks_read+blks_hit+0.000001)*100.0 as numeric(5,2)) as cache, cast(xact_commit/(xact_rollback+xact_commit+0.000001)*100.0 as numeric(5,2)) as success from pg_stat_database a order by a.datname"

# total databases size
UserParameter=pgsql.db.totalsize,psql -Atc "select sum(pg_database_size(datid)) as total_size from pg_stat_database"

# specific database size (in bytes)
UserParameter=pgsql.db.size[*],psql -Atc "select pg_database_size('$1') as size"

# database cache hit ratio (percentage)
UserParameter=pgsql.db.cache[*],psql -Atc "select cast(blks_hit/(blks_read+blks_hit+0.000001)*100.0 as numeric(5,2)) as cache from pg_stat_database where datname = '$1'"

# database success rate (percentage)
UserParameter=pgsql.db.success[*],psql -Atc "select cast(xact_commit/(xact_rollback+xact_commit+0.000001)*100.0 as numeric(5,2)) as success from pg_stat_database where datname = '$1'"


After restarting ZABBIX Agent, you can check whether the added parameters are valid by issuing zabbix_get command in a shell. For instance, to query PostgreSQL instance version, type this:


$ zabbix_get -s localhost -k pgsql.version
PostgreSQL 8.3.3 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu7)

This other script returns an overview of existing databases in the instance, highlighting their names, size in disk, cache hit ratio and percentage of successful transactions:


$ zabbix_get -s localhost -k pgsql.db.summary
datname | size | cache | success
-------------+---------+-------+---------
auction5 | 4400 kB | 0.00 | 0.00
auditing | 4512 kB | 0.00 | 0.00
escola | 4656 kB | 0.00 | 0.00
postgres | 4223 kB | 99.81 | 100.00
rodrigo | 4144 kB | 0.00 | 0.00
template0 | 4144 kB | 0.00 | 0.00
template1 | 4144 kB | 0.00 | 0.00
zabbix | 10 MB | 99.99 | 100.00
zahle | 86 MB | 0.00 | 0.00
(9 registros)

In order to measure total space in disk occupied by the entire instance, this script should be used:


$ zabbix_get -s localhost -k pgsql.db.totalsize
1507326452

On the other hand, to retrieve the space (in bytes) occupied by a single database, you just need to specify its name in the parameter key, as exemplified below:


$ zabbix_get -s localhost -k pgsql.db.size[auction5]
4505604

Likewise, to recover cache hit ratio (in percentage) of a single database, use this key:


$ zabbix_get -s localhost -k pgsql.db.cache[zabbix]
99.99

The percentage of successful transactions in relation to all attempts is given by this parameter:


$ zabbix_get -s localhost -k pgsql.db.success[postgres]
100.00


After testing these parameters, it is time to set them up onto ZABBIX Frontend as illustrated below:



It is very interesting to further add some traps and actions based on the configured items. For example, to send an email to the DBA every time a given database grows up faster than expected or whether its cache ratio starts to lower significantly.

At Joe Uhl's blog there is a post [4] concerning using ZABBIX to monitor PostgreSQL TPS (Transactions per Second). It is an interesting source as it explains how to configure deltas and graphs in ZABBIX.

References:


[1] ZABBIX Monitoring Solution
[2] Cedrus: PostgreSQL Manager
[3] ZABBIX Wiki - PostgreSQL UserParameters
[4] Monitoring PostgreSQL TPS with Zabbix

terça-feira, 1 de dezembro de 2009

Creating Demoiselle JPA-compliant DAOs



This article aims at providing a walk through on building Data Access Object (DAO) [1] components using Java Persistence API (JPA 1.0) [2], since the latter is now fully supported in the 1.1 release of Demoiselle Framework [3].

The reference source codes used in the following sections were retrieved from Auction5 [4], a sample web application specifically designed to exemplify JPA usage along with Demoiselle.


i. Introducing support elements


The main characters on the new Demoiselle persistence handling mechanism are IDAO [5] and JPAGenericDAO [6].

IDAO is a simple interface containing data manipulation and retrieval methods. It is, above all, a markup interface intended to be implement by whoever class playing a DAO role. Therefore, no matter whether the final class will handle objects from a DBMS or an XML file, using pure JDBC, Hibernate or JPA, it must necessarily implement IDAO.

On the other hand, JPAGenericDAO is an abstract class intented to leverage productivity by providing the derived class with a bunch of persistence-related ready-to-use methods such as: findById(), findByJPQL(), insert(), update(), and remove().


ii. Defining entities


Entity classes are usually the first elements to be created on a Java application powered by JPA persistence.

In the class diagram below the whole application entities considered in the examples are depicted.




iii. Designing interfaces


In order to build DAO artifacts on Demoiselle, one needs to previously create their respective interfaces. Besides, these interfaces must extend IDAO by providing the targeted entity class in the generics brackets (e.g. <Auction>).

The signatures of additional needed methods must be indicated on each interface, remembering that all methods provided by IDAO are already implicitly included.

For instance, for the Auction entity, there must be created IAuctionDAO interface by extending IDAO and declaring additional listing methods. Its final code is shown below:


public interface IAuctionDAO extends IDAO<Auction> {
public Auction findById(Long id);
public List<Auction> listOpenAuctionsByCategory(Category category);
public List<Auction> listNewestAuctions(int quantity);
public List<Auction> listMostOfferedAuctions(int quantity);
public List<Auction> listEndingSoonAuctions(int seconds, int quantity);
public List<Auction> listCheapestPriceAuctions(int quantity);
public List<Auction> listOpenAuctionsByItem(Item item);
public List<Auction> listOpenEndedAuctions(Date timestamp);
}

Likewise, interfaces for Bid, Category, Item, and Order entities suggest the structures below:


public interface IBidDAO extends IDAO<Bid> {
public List<Bid> listLastBidsForAuction(Auction auction, int quantity);
}

public interface ICategoryDAO extends IDAO<Category> {
public Category findById(Short id);
public List<Category> listAvailableCategories();
public List<Category> listAllCategories();
}

public interface IItemDAO extends IDAO<Item> {
public Item findById(Integer id);
public List<Item> listByCategory(Category category);
}

public interface IOrderDAO extends IDAO<Order> {
public Order findById(Long id);
public List<Order> listOrdersByLogin(String login);
}

On an application powered by Demoiselle all those interfaces are usually placed on a subpackage named "persistence.dao" inside the main package tree (e.g. "br.gov.demoiselle.sample.auction5.persistence.dao").


iv. Designing implementation classes


After creating the DAO interfaces, the next step is to build their respective implementation classes. For each interface, there must be created a concrete class declared with a reference to that interface.

In addition, in order to boost development time and yet help the developer with several coding facilities, these new classes should extend JPAGenericDAO superclass described previously.

Thus, DAO implementation classes should contain the declarations below:


public class AuctionDAO extends JPAGenericDAO<Auction> implements IAuctionDAO { ... }

public class BidDAO extends JPAGenericDAO<Bid> implements IBidDAO { ... }

public class CategoryDAO extends JPAGenericDAO<Category> implements ICategoryDAO { ... }

public class ItemDAO extends JPAGenericDAO<Item> implements IItemDAO { ... }

public class OrderDAO extends JPAGenericDAO<Order> implements IOrderDAO { ... }


As a result of "implements", every method declared in an interface must be implemented and fulfilled with actual code by the developer in the class. However, there are exceptions: methods belonging to IDAO are already implemented in JPAGenericDAO, and therefore do not need to be reimplemented.

Demoiselle Framework suggests hosting these classes onto a subpackage named "implementation" right inside the subpackage containing the interfaces (e.g. "br.gov.demoiselle.sample.auction5.persistence.dao.implementation").


v. Implementing classes methods


In the next sections there will be explored several ways of implementing the methods declared in the interfaces, by using existing code, by issuing JPQL queries and even through SQL native queries.


1. Superclass methods


The first alternative on building persistence methods in the DAO is the simplest one: to directly invoke existing methods of JPAGenericDAO superclass.

For example, a listAllCategories() method designed to bring all categories could be coded in CategoryDAO using the following contents:


public List<Category> listAllCategories() {
return findAll();
}

If the method name is to be kept, one should use the "super" reference variable to prevent infinite loops, as illustrated below in findById() method for CategoryDAO:


public Category findById(Short id) {
return super.findById(id);
}



2. JPQL fixed string


As an evolution to EJB QL, JPA 1.0 introduces a query language called JPQL (Java Persistence Query Language). This language's synthax is similar to Hibernate's HQL and offers the same purpose: to send instructions to the DBMS by using a language closer to object-oriented world.

It is made available to the JPAGenericDAO class a method called findByJPQL(), which internally creates a JPQL query according to a given string, executes the query, builds a list of entities and finally returns the latter in a type-safe fashion.

When coding a DAO method on Demoiselle, we could use fixed JPQL queries whenever there is no need to give parameters to them. The CategoryDAO implementation class exhibits the usage of fixed string JPQL queries through findByJPQL():


public List<category> listAvailableCategories() {
return findByJPQL("select c from Category c where c.active = true order by c.name");
}



3. JPQL with positional parameters


As most of our real-world queries involve passing parameters to them, JPQL also offers the possibility of doing this. Actually, there are two ways of passing parameters to a query: using positional (numbered) or named parameters.

Positional parameters will be explored in the current section. They are expressed in the JPQL string with the "?" character followed by an integer number starting from 1.

On JPAGenericDAO the method findByJPQL() is overloaded so that it can handle positional parameters as well. The AuctionDAO implementation class exhibits the usage of JPQL queries with positional parameters on listAllAuctionsByItem() method:


public List<Auction> listAllAuctionsByItem(Item item) {

String jpql = "select a from Auction a where a.item = ?1";

List<Auction> result = findByJPQL(jpql, item);

return result;
}

Demoiselle Framework introduces pagination mechanisms through Page and PagedResult classes. In order to use them, one needs to invoke an other overloaded version of findByJPQL(), which accepts a Page object as the second argument. The BidDAO implementation class comes with listLastBidsForAuction() method, which exemplifies pagination of results using the classes described and yet accepts a positional parameter:


public List<Bid> listLastBidsForAuction(Auction auction, int qty) {

Page page = new Page(qty);

String jpql = "select b from Bid b where b.auction.id = ?1 order by b.timestamp desc";
PagedResult<Bid> pr = findByJPQL(jpql, page, auction.getId());

return (List<Bid>) pr.getResults();
}



4. JPQL with named parameters


There are some situations where numbered parameters in a query might be a confusing or not preferred alternative. In order to handle it, JPA specification foresees named parameters on JPQL queries.

According to JPA, a named parameter in JPQL is indicated with the ":" character followed by an unique string which specifies it.

Inside Demoiselle, using named parameters consists of a map instantiation and the execution of another overloaded findByJPQL(), this time receiving in addition a Map object as argument. The AuctionDAO implementation class exhibits the usage of JPQL queries with bound named parameters:


public List<Auction> listOpenAuctionsByCategory(Category category) {

String jpql =
"select a from Auction a " +
"where a.status = :status " +
" and a.deadline > :deadline " +
" and a.item.category = :category " +
"order by a.item.description";

Map<String, Object> params = new HashMap<String, Object<();
params.put("status", Status.OPEN);
params.put("deadline", new Date());
params.put("category", category);

List<Auction> result = findByJPQL(jpql, params);

return result;
}



5. Named JPQL query


As an alternative introduced by Hibernate, JPA brings a feature called named queries, which aims at writing complex JPQL statements inside entity classes in the form of annotations. These so-called annotated queries are then referenced back when issuing JPQL queries.

JPAGenericDAO comes handy once again by providing another method: findByNamedQuery(). This method receives a string containing the name of a JPQL query whose statement is already on an entity. For instance, let the Item entity with a @NamedQuery annotation right above its class declaration:


@NamedQuery(
name = "itemsByCategory",
query = "select i from Item i where i.category.id = ?1 order by i.description"
)
public class Item implements IPojoExtension { ... }

Then, the respective ItemDAO implementation class should make use of JPAGenericDAO's findByNamedQuery() as shown in the code belonging to listByCategory() method:


public List<Item> listByCategory(Category category) {
return findByNamedQuery("itemsByCategory", category.getId());
}

Note that in the previous example a positional parameter was placed. But what if we need named parameters alongside named queries? The answer is: an overloaded version of findByNamedQuery() might take place! For example, the Auction entity presents a @NamedQuery according to the code below:


@NamedQuery(
name = "openAuctionsByItem",
query = "select a from Auction a where a.status = :status and a.deadline > :deadline and a.item = :item"
)
public class Auction implements IPojoExtension { ... }

Observe that named parameters on a named JPQL query are indicated in the annotation as if they were to be used by findByJPQL() method. And then, in the same manner, we need to create a map object in order to invoke the method, which this time is findByNamedQuery(). Take a look at the contents of AuctionDAO, particularly the listOpenAuctionsByItem() method:


public List>Auction< listOpenAuctionsByItem(Item item) {

Map<String, Object> params = new HashMap<String, Object<();
params.put("status", Status.OPEN);
params.put("deadline", new Date());
params.put("item", item);

List<Auction> result = findByNamedQuery("openAuctionsByItem", params);

return result;
}



6. Native SQL query


So, are we constrained to JPQL statements and object-oriented queries when adopting JPA persistence mechanism on a Java application? Not at all!

There are some cases where performance matters most of everything and the only possibility of achieving it is by executing native SQL instructions, usually made through JDBC in the Java world. This particular approach can boost up SQL execution time, especially for those who prefer to create their own database stored procedures or functions.

Despite this development behavior can lead to a given DBMS dependency and thus compromising application portability, the architect still might choose to create native SQL queries. JPA specification was developed with a rich set of components (i.e. interfaces and annotations) concerning the invocation of native SQL instructions.

In Demoiselle, once again, JPAGenericDAO can help the developer with a pre-built method: findByNativeQuery(). Take the example of the listMostOfferedAuctions() method inside AuctionDAO, which contains SQL directed to PostgreSQL DBMS:


public List<Auction> listMostOfferedAuctions(int quantity) {

String sql =
"select d.* from ( " +
" select b.auction_id, count(b.id) " +
" from bids b " +
" inner join auctions a on (a.id = b.auction_id) " +
" where a.status = ?1 and a.mode <> ?2 " +
" group by b.auction_id " +
" order by count(b.id) desc " +
" limit ?3) c " +
"join auctions d on (d.id = c.auction_id) " +
"where d.bestbid_id is not null " +
"order by c.count desc";

List<Auction> result = findByNativeQuery(sql,
Status.OPEN.ordinal(), Mode.SELLING.ordinal(), quantity);

return result;
}

Another method of AuctionDAO, listEndingSoonAuctions(), makes use of native SQL query and positional parameters:


public List<Auction> listEndingSoonAuctions(int seconds, int quantity) {

String sql =
"select * " +
"from auctions " +
"where status = ?1 " +
" and deadline - current_timestamp between '0 s' and cast(?2 as interval) " +
"order by deadline " +
"limit ?3";

List<Auction> result = findByNativeQuery(sql,
Status.OPEN.ordinal(), seconds + " s", quantity);

return result;
}

But now another issue is raised: what if we need native SQL query along with named parameters? The answer might be simple: just merge both techniques into one, right? Well, partially.

According to JPA 1.0 specification, a named parameter is an identifier that is prefixed by the ":" symbol, and its use applies just to JPQL, therefore not being defined for native queries. Thus, only positional parameter binding may be portably used for native queries.

Despite that lack of portability, JPA implementations usually offer named parameters on native queries. Unfortunately there is no common sense yet, so that implementations consider "?" or ":" symbols when defining named parameters in a native SQL string. This is better illustrated in the code below, taken from AuctionDAO's listCheapestPriceAuctions() method, which is prepared to run on Hibernate EntityManager JPA implementation:


public List<Auction> listCheapestPriceAuctions(int quantity) {

// WARNING: prepared for Hibernate - use "?" instead of ":" for TopLink or EclipseLink
String sql =
"select d.* from (" +
" select a.id, coalesce(b.amount, a.startingprice, a.sellingprice) as price " +
" from auctions a left join bids b on (b.id = a.bestbid_id) " +
" where a.status = :status " +
" order by 2 asc " +
" limit :quantity) c " +
"join auctions d on c.id = d.id " +
"order by c.price";

Map<String, Object> params = new HashMap<String, Object<();
params.put("status", Status.OPEN.ordinal());
params.put("quantity", quantity);

List<Auction> result = findByNativeQuery(sql, params);

return result;
}

Thus, portability is compromised in the previous example, as we need to take special precautions when switching to another JPA implementation, say EclipseLink or Apache OpenJPA.


7. Named native SQL query


As with JPQL, it is possible to store the entire native query statement onto an entity class and then reference it as needed. In order to do that, JPA defines another annotation, @NamedNativeQuery, which has the same purpose of @NamedQuery. After setting the named native query, one could invoke findByNamedQuery() method to execute it and possibly bring back the results. Note that this very same method fits for both JPQL and native SQL queries.

So, consider the named native query "newestAuctions", which is defined on Auction entity file. There, in the @NamedNativeQuery annotation three parameters must be passed: the query unique identifier, the statement itself, and a class indicating the result type. The referenced code is shown below:


@NamedNativeQuery(
name = "newestAuctions",
query = "select * from auctions where status = ?1 order by creation desc limit ?2",
resultClass = Auction.class
)
public class Auction implements IPojoExtension { ... }

At DAO implementation class AuctionDAO this named native query is called inside listNewestAuctions() method, as illustrated in the following code:


public List<Auction> listNewestAuctions(int quantity) {
List<Auction> result = findByNamedQuery("newestAuctions",
Status.OPEN.ordinal(), quantity);
}



8. LIQUidFORM tool


JPA 1.0 specification does not provide a Criteria API [7], a feature introduced by Hibernate designed to help developers on building HQL queries. With that we avoid string concatenation and rather we declaratively define the statement, thus reducing the risk of errors in its synthax.

Fortunately JPA 2.0 was conceived with a Criteria API, thus soon JPA implementations will run after that. Meanwhile, a lot of alternatives were developed to fill in such requirement in JPA. Amongst them, one particular tool was employed in the sample code: LIQUidFORM [8].

LIQUidFORM is actually a set of classes and interfaces designed to compose JPQL statements in a type-safe and refactoring proof style. It might be strange at first sight, as it contains methods called select(), from(), and where(), but it is indeed a very interesting idea.

The key to LIQUidFORM is to make use of static imports in the class header, namely the lines below:


import static com.google.code.liquidform.LiquidForm.*;
import static com.google.code.liquidform.Parameters.*;

OrderDAO implementation class exhibits the usage of LIQUidFORM tool for creating a JPQL query inside listOrdersByLogin() method, as shown below:


public List<Order> listOrdersByLogin(String login) {

Order o = alias(Order.class, "o");

String jpql =
select(o).
from(Order.class).as(o).
where(eq(o.getLogin(), param(1, String.class))
).toString();

return findByJPQL(jpql, login);
}



vi. Conclusion


In this article we have explored some benefits of building the persistence layer of a Java application by adopting Java Persistence API (JPA 1.0) inside an architecture composed by DAO-based interfaces and implementation classes.

In addition, we have seen the usage of Demoiselle Framework structural IDAO interface and JPAGenericDAO support class when building methods designed to manipulate data through JPA. Those methods also served to exemplify the existing JPA approaches by introducing JPQL and native SQL queries, positional and named parameters, JPQL and SQL named queries, and LIQUidFORM tool.


vii. References


[1] Core J2EE Patterns - Data Access Object
[2] JPA 1.0 (JSR 220)
[3] Demoiselle Framework
[4] Auction5 Application Sample
[5] IDAO interface
[6] JPAGenericDAO class
[7] Criteria API
[8] LIQUidFORM

quinta-feira, 26 de novembro de 2009

Roteiro de voo CWB-CGH




Depois que viciei no M$ Flight Simulator, comecei a prestar atenção nos comandos efetuados pelos pilotos em voos comerciais. A seguir um roteiro de eventos que registrei a partir da janelinha de um A319 de Curitiba (CWB) a São Paulo (CGH)... :) Mapa da rota ao lado.




tempo evento
-------- ----------------------
19:25:00 entrada (gate 2 CWB)
19:40:15 portas auto
+30" pushback
19:44:15 throttle up
45:45 flaps down
+45" taxi
55:50 entrada pista 15C
strobes on
58:00 alinhamento
+1' speed up
+1' pitch up
+15" gears up
+1' flaps up 100%
20:02:45 pitch down slowly
04:15 bank left
+30" stabilize (NE)
22:45 início descida
25:00 avistado litoral SP
29:30 bank left (N)
30:00 landing lights on
31:10 flaps down +1
37:45 bank right
+15" stabilize
+15" flaps down +1
40:15 bank right
+20" stabilize
40:30 bank right
+20" stabilize
42:40 bank right
+20" stabilize
45:00 throttle down
+45" slats down
+10" flaps down +1
+10" flaps down +1
+5" pouso autorizado
47:35 avistado Tietê
49:45 avistada Av. Bandeirantes
50:15 tocada pista 17R CGH
+5" spoilers/reversos acionados
+30" saída pista principal
+10" taxi
+20" flaps/spoilers recolhidos



PS: Sim, não há registro do abaixamento do trem de pouso. Simplesmente porque não percebi quando ele ocorreu... :(

quinta-feira, 19 de novembro de 2009

Seattle-Vancouver on TAM's A319



From Seattle-Tacoma International Airport [1] to Vancouver International Airport [2] on a TAM Airlines [3] Airbus A319 [4] aircraft... :D









References:
[1] http://en.wikipedia.org/wiki/Seattle-Tacoma_International_Airport
[2] http://en.wikipedia.org/wiki/Vancouver_International_Airport
[3] http://en.wikipedia.org/wiki/TAM_Airlines
[4] http://en.wikipedia.org/wiki/Airbus_A319#A319

segunda-feira, 16 de novembro de 2009

Unicode to Java string literal converter



I was having some headaches writing properties files valid across several platforms due to encoding issues. However, by googling around today I found this very interesting JavaScript-based conversion tool:

http://www.snible.org/java2/uni2java.html

Thanks to Ed Snible!

terça-feira, 10 de novembro de 2009

Resumos das palestras do TDC 2009



Seguem resumos das palestras dos keynotes no The Developer's Conference 2009 - Floripa [1] ministradas no dia 9 de novembro.

Tendências em Java EE: Como serão os próximos 5 anos - Rod Johnson

O criador do Spring [2], um dos mais populares frameworks em Java, apresentou nesta palestra suas previsões para o futuro da tecnologia da informação como um todo. Nessa linha, afirmou que "estamos a ponto de presenciar a maior ruptura em TI desde o declínio do mainframe", impulsionada por forças como Cloud Computing, Lei de Moore e o fato de os custos estarem se voltando para as pessoas em contraposição ao hardware.

Disse também que sistemas gerenciadores de bancos de dados relacionais (SGBDRs) não irão embora, e ao invés disso haverá grande ascensão da chamada "persistência poliglota", onde haverão diferentes meios de se armazenar os dados. (Aqui entra a JPA!)

Sobre a computação em nuvem, afirmou que ela está mais próxima que SOA e que possui as seguintes vantagens características: ser abstrata, integrada e opinativa, dinamicamente escalável e consumida como um serviço, além de inevitável devido a questões econômico-ambientais. E com isso, disse que nós desenvolvedores nos tornaremos mais poderosos! :) Precisaremos fornecer APIs direcionadas para a nuvem...

Com relação aos frameworks, iniciou com a frase "Frameworks can't be too disruptive!", alegando que o código aberto possibilitou a criação de excelentes soluções de software, mas por outro lado fez do Java uma plataforma confusa para os iniciantes.

Finalizou dizendo que nessa era de desordem a abstração será mais importante do que nunca, citando como possíveis gerenciadoras para esse caos no software as seguintes soluções: Grails, Spring, Roo e Rails, além de especificações como a JPA.


Java EE 6 - Uma grande evolução - Mike Keith

O Mike é o líder de importantes especificações do Java, tal como EJB 3 e JPA, e iniciou esta palestra apresentando os objetivos e destaques para a esperada versão 6 do Java EE [3], especialmente JSF 2.0, Servlet 3.0, EJB 3.1 e JPA 2.0.

Em seguida resumiu cada uma das novas funcionalidades: Java Profiles, JNDI Namespaces, Data Source Definition, novas anotações para Web (ex: @WebServlet, @ServletFilter, @WebServletContextListener), Web Fragments, Async Processing (mesmo para session beans!). Além disso, para o EJB 3.1 teremos: interfaces opcionais, singleton, concorrência, timer estilo cron, EJB lite, contêiner embutido e novas regras de empacotamento.

A JPA 2.0 terá cache compartilhado, melhorias na JPQL e a inclusão de uma API de Criteria. Os Managed Beans substituirão os MBs atuais do JSF e haverão anotações para injeção comuns a JSE e JEE, além do início de sua padronização. Os WebBeans serão contemplados pela CDI 1.0, a qual basicamente visará gerenciar objetos injetáveis ligados a contextos do ciclo de vida, possuindo anotações como @Inject, @Qualifier, @Scope, @Named e @Singleton.

Outra questão importante refere-se ao processo de limpeza, em que determinadas tecnologias tornaram-se obsoletas e entrarão no estágio de poda (pruning stage). Entre elas citou JAX-RPC, EJB Entity Beans, JAXR e JSR-88 Deployment. Os fornecedores não serão obrigados a implementá-las em releases futuras.

Por fim, a notícia que mais aguardávamos, a data do lançamento. Mike disse que os grupos de experts das JSR praticamente já concluíram seus trabalhos e que a Java EE 6 será liberada no dia 10 de dezembro!


JSF 2.0: Uma Abordagem Completa - Ed Burns



Ed iniciou a palestra fazendo uma pequena introdução sobre a tecnologia JavaServer Faces a fim de balizar o conhecimento do público. Apresentou o ciclo de vida no processamento de requisições do JSF e as funcionalidades desejadas (conversão e validação de dados, fluxo de páginas, integração com banco de dados, desempenho, segurança, etc).

Enfatizando os 4 pilares do JSF 2.0 (visão, interação com o modelo, navegação e ciclo de vida), destacou que as visões passarão a ser páginas Facelets, haverá a anotação @ManagedBean, e que o JSP será depreciado.

A Expression Language será mais poderosa, e na navegação de páginas o uso de XMLs será opcional, devido à nova navegação implícita, altamente dinâmica e especialmente útil com os novos objetos do tipo "flash". Com relação ao ciclo de vida, haverá um excelente suporte para o uso de Ajax, além do advento dos System Events, finos ouvintes dos eventos do ciclo.


Spring Roo - Rod Johnson

Em substituição ao Chris Schalk da Google, Rod fez uma palestra prática totalmente sem slides apresentando uma nova solução da SpringSource: o Spring Roo [5].

Trata-se de um gerador de código em Java que como qualquer do gênero visa impulsionar a produtividade do desenvolvimento. Achei muito semelhante ao Ruby on Rails: um prompt de comando (shell) que executa diversas tarefas, especialmente a criação automática de inúmeros arquivos e o fornecimento de um servidor Web simples para os testes. Funciona até auto-completion no estilo bash!

A instalação do Roo é extremamente simples, mas ele exige que tenhamos o seguinte ambiente: Eclipse IDE com plugins WTP e AJDT. Isso mesmo, o Spring Roo usa extensivamente o AspectJ! São criados arquivos ".aj" a fim de incluir funcionalidade as novas classes.

Outras características importantes foram a criação automática de testes unitários, o fato de o projeto ser gerenciado pelo Maven (são criados diversos arquivos "pom.xml") e a persistência ser manipulada através de JPA.

A mágica do Roo acontece durante o gerenciamento das entidades. Por exemplo, os métodos persist() e findAll() são inseridos em cada entidade através de injeção de aspectos! Achei um tanto bizarro, mas não deixa de ser uma possibilidade interessante. As entidades não precisam declarar métodos getters e setters e nem toString() customizado, pois, novamente, aspectos fazem isso..!

Eis um exemplo de classe criada no Roo:


@Entity
@RooJavaBean
@RooToString
@RooEntity
public class Restaurant {

private String name;

}

Tal como no Rails, podemos criar com uma agilidade e rapidez descomunal uma aplicação completa incluindo scaffold (i.e. o CRUD), testes unitários, páginas web para apresentação (em JSF!) e toda a configuração da persistência.

Atualmente o Spring Roo está na release candidate 2 e tem previsão de ser liberado no final desse ano. Infelizmente ainda não tem suporte para o NetBeans, devido ao fato de depender do Eclipse AJDT.

Referências:
[1] TDC 2009 Floripa
[2] Spring Framework
[3] Java EE 6 Technologies
[4] JavaServer Faces Technology
[5] Spring Roo

sábado, 7 de novembro de 2009

Auction5 sample web application released



I'm very proud to announce that Auction5, a sample web application built under Demoiselle Framework [1] has just been released to the public. Its complete documentation including source codes retrieval and deployment instructions can be found here: [2].

Auction5 is a MVC-structured application that exemplifies an online auction system using Demoiselle Framework and related technologies. Its main goal is to provide a complete transactional application based on JavaServer Faces (JSF 1.2 [3]) and Java Persistence API (JPA 1.0 [4]) specifications.


Moreover, the sample assembles several top computing technologies such as Java EE, Maven, JBoss Application Server, Apache Tomcat, and PostgreSQL DBMS.

Please send me your comments and or suggestions.

References:
[1] Demoiselle Framework
[2] Auction5 Application Sample
[3] JavaServer Faces 1.2
[4] Java Persistence API 1.0

quarta-feira, 28 de outubro de 2009

Script em Shell para monitoração de resultado de concurso



Eis um script em Shell simples para automatizar o acompanhamento de resultados de concurso pela Internet.

É só configurar a URL desejada e o intervalo entre as verificações (atualmente 15 minutos) e em seguida botar esse negócio para rodar em um terminal. No momento que aparecer alguma novidade o Firefox irá "popar"!

#!/bin/bash

URL="http://www.cops.uel.br/?content=concursos/81_celepar/index.html"

while true
do
 echo "Recuperando página..."
 wget $URL -O res.html
 if [ $? -ne 0 ]; then continue; fi

 echo "Comparando arquivos..."
 if [ -f ult.html ]
 then
  diff res.html ult.html > /dev/null
  dif=$?
 else
  dif=0
 fi

 mv res.html ult.html

 if [ $dif -ne 0 ]
 then
  echo 'Saiu o resultado!'
  firefox $URL
  break
 fi

 echo "Aguardando..."
 sleep 900 # 15 min
done

exit 0


Use-o com moderação. :D

quarta-feira, 7 de outubro de 2009

PGCon Brasil 2009



Pessoal, faltam só alguns dias para o PGCon Brasil 2009, o maior evento de PostgreSQL da América Latina!


Confira maiores informações no site do evento:

[1] http://pgcon.postgresql.org.br/2009/

sexta-feira, 2 de outubro de 2009

Poster Motivacional: Fiat Lux!



science

quinta-feira, 24 de setembro de 2009

Demoiselle JPA in a Nutshell



One of the greatest new features coming to 1.1 release of Demoiselle Framework [1] is the ability of using JPA [2] (Java Persistence API) in a straightforward manner. JPA specification [3] is a trend in Java development and is often recommended to be used in new applications.

In this article we'll explore the technical steps involved on developing the simplest DAO [4] (Data Access Object) implementation classes with the new JPA support components.

So, what's new in Demoiselle concerning JPA? First of all, there is a new injection aspect towards IDAO [5] interfaces. Basic classes implementing IDAO interface should now simply have an attribute of type EntityManager [6] annotated by @Injection and then with the aid of AspectJ engine and some proxy classes the magic happens!

For instance, consider the following IDepartmentDAO interface in order to manage a Department entity:

public interface IDepartmentDAO extends IDAO<Department> {

    public Department findById(Short id);

}

The respective implementation class DepartmentDAO should have the structure below:

public class DepartmentDAO implements IDepartmentDAO {

    @Injection
    private EntityManager em;

    public Department findById(Short id) {
        return em.find(Department.class, id);
    }

    public boolean exists(Department pojo) {
        return em.contains(pojo);
    }

    public Object insert(Department pojo) {
        em.persist(pojo);
        return pojo;
    }

    public void update(Department pojo) {
        em.merge(pojo);
    }

    public void remove(Department pojo) {
        em.remove(pojo);
    }

}

Simple, isn't it?

EntityManager is the interface used to interact with the persistence context in JPA. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed. This interface defines the methods that are used to interact with the persistence context. The EntityManager API is used to create and remove persistent entity instances, to find entities by their primary key, and to query over entities.

Within a given unit of work, such as an HTTP request in web applications, a single instance of EntityManager is created and automatically injected on every DAO class that is referenced on that thread. That EntityManager is then closed as the transaction is finished.

Moreover, even outside a complete and robust Java EE application, i.e. without an EJB [3] container, the developer could make easy use of JPA in Demoiselle!

In order to turn it even simpler for the developer, the DAO implementation could still make use of the new JPAGenericDAO [7] support class. Thus, DepartmentDAO could have the following code:

public class DepartmentDAO extends JPAGenericDAO<Department>
    implements IDepartmentDAO {

    public Department findById(Short id) {
        return super.findById(id);
    }

}

In other words, there is no need anymore of storing an EntityManager field and to implement all IDAO methods. That is, there will be almost nothing left to do but customized business rules methods!

If you want to know more about the framework technical details, there will be another article explaining the whole architecture behind the scenes. :D

References:
[1] Demoiselle Framework
[2] The Java Persistence API
[3] EJB 3.0 JPA Specification
[4] Data Access Object
[5] IDAO interface
[6] EntityManager interface
[7] JPAGenericDAO class


terça-feira, 8 de setembro de 2009

Tips on PostgreSQL tuning



At last I found something more concise towards PostgreSQL DBMS fine tuning. :)

http://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server

Cedrus PostgreSQL Management versão 2.0




Pessoal, estou organizando esforços para o desenvolvimento da segunda geração do Cedrus [1], um gerenciador para o SGBD PostgreSQL criado em 2006 na CELEPAR e apresentado ao público no CONISLI [4] naquele mesmo ano em São Paulo.


Dessa vez será adotada a plataforma Launchpad para o gerenciamento do projeto. Se você tiver interesse em participar de qualquer das seguintes etapas (requisitos, projeto, desenvolvimento ou testes) ou se desejar somente dar sugestões, mesmo se você só tiver experiência com administração de outros SGBDs, sinta-se livre em conhecer o projeto [2] ou em fazer parte da equipe [3]!


Referências:


[1] Cedrus 1.0 no SourceForge
[2] Cedrus 2.0 no Launchpad
[3] Equipe do Cedrus no Launchpad
[4] CONISLI - Congresso Internacional de Software Livre

terça-feira, 18 de agosto de 2009

Plugins essenciais para o Eclipse...



Eis alguns plugins interessantes e às vezes indispensáveis para a IDE Eclipse [1]:
  • m2eclipse [2]: integração do Maven 2 com o Eclipse
  • JBoss Tools [3]: utilitários para desenvolvimento de projetos Web
  • AJDT [4]: ferramentas de desenvolvimento com AspectJ (AJ)
  • Subclipse [5]: suporte ao controle de versionamento Subversion (SVN)



Lembre-se de que o ideal é sempre instalar os plugins via Update Site...

Referências:

[1] http://www.eclipse.org/
[2] http://m2eclipse.sonatype.org/
[3] http://www.jboss.org/tools/
[4] http://www.eclipse.org/ajdt/
[5] http://subclipse.tigris.org/

terça-feira, 4 de agosto de 2009

findjar.com: Encontre o jarro perdido!



O problema: java.lang.ClassNotFoundException


Como desenvolvedor Java, você com certeza passou por isso alguma vez...
Eis um exemplo de stack trace em que ocorre este tipo de exceção:


Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 21 more


Para resolver isso, é preciso encontrar o arquivo JAR em que a classe referenciada se encontra e colocá-lo no classpath da aplicação que está sendo executada. Mas e se não soubermos onde fica essa classe...?

A solução 1: grep


Se estiver com sorte, você já possui a biblioteca que contém essa classe. Neste caso, no Linux basta executar um rgrep ou um grep -r e copiar o arquivo da biblioteca.

A solução 2: findjar.com


Caso você ainda não possua a biblioteca localmente, é preciso saber o nome do arquivo JAR e onde encontrá-lo na Internet...

Para a primeira pergunta, o site findJAR.com[1] é um grande auxílio. Trata-se de um motor de busca que nos ajuda a encontrar arquivos do tipo JAR que contêm as classes Java requeridas. Basta digitar o nome (simples ou qualificado) da classe que você precisa e iniciar a busca!

findJAR.com é uma mão na roda para resolver com facilidade problemas do tipo NoClassDefFoundError e ClassNotFoundException.

Referências:


[1] http://www.findjar.com

domingo, 2 de agosto de 2009

Encoding UTF-8 no Tomcat 6.0



Ao utilizar o servidor Apache Tomcat 6.0 [1] para o deploy de aplicações Web em Java utilizando o encoding Unicode (UTF-8) [2] podemos enfrentar pequenos problemas quando existe entrada de dados pelo usuário.

Por exemplo, considere a aplicação a seguir que faz uso de um formulário para o cadastro de categorias de produtos:



A anomalia ocorre durante o processo de envio da requisição HTTP do tipo POST [3]. Veja na imagem a seguir o problema na acentuação resultante do formulário:



Uma possível solução para este problema consiste na criação de um filtro, ou Filter [4], um padrão de projeto da especificação Java EE. A função deste filtro é de interceptar as requisições HTTP e forçar a codificação de caracteres para a que for parametrizada (no exemplo, a UTF-8).

A primeira etapa envolve a criação de uma nova classe na aplicação, cujo nome será EncodingFilter e a qual deverá implementar a interface Filter [5] da especificação Java Servlet. Desta forma, o arquivo EncodingFilter.java terá o seguinte conteúdo:


package com.hsiconsultoria.zabumba.filter;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class EncodingFilter implements Filter {

private String encoding;

public void init(FilterConfig config) throws ServletException {
this.encoding = config.getInitParameter("encoding");
}

public void destroy() {
}

public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {

request.setCharacterEncoding(encoding);

chain.doFilter(request, response);
}

}

Em seguida é preciso indicar a existência desse novo filtro no arquivo WEB-INF/web.xml, também conhecido como Deployment Descriptor [6]. É neste arquivo que o filtro será devidamente configurado para interceptar as requisições HTTP na aplicação Web.

Sendo assim, basta incluir as linhas seguintes no arquivo web.xml:



EncodingFilter
EncodingFilter

com.hsiconsultoria.zabumba.filter.EncodingFilter


filtro para codificação dos caracteres
encoding
UTF-8



EncodingFilter
/*


Para finalizar, é só salvar tudo e reinicializar o Tomcat. Veja na tela abaixo o resultado dessa alteração na aplicação:



Referências:
[1] Apache Tomcat 6.0
[2] Formato UTF-8
[3] HTTP/1.1 Method Definitions
[4] Intercepting Filter Pattern
[5] Interface Filter
[6] Java EE Deployment Descriptors

quinta-feira, 25 de junho de 2009

C'est où le son dans Firefox en Ubuntu ?



Même avec la présence du plugin VLC [1] pour Firefox sur Linux, le son ne marche pas tout simplement...

À travers l'adresse secret "about: config" Firefox, nous avons la liste des plugins installés dans le navigateur, notamment le plugin VLC (qui permet des extensions de fichiers tels que .OGG) :



Néanmois, il nous reste mettre en place le wrapper ALSA [2] pour des applications OSS [3].

Au Linux il existe le paquet "alsa-oss" qui fournisse précisément cette fonctionnalité ! Ainsi, il faut lancer l'instruction suivante en tant que "root" :

# apt-get install alsa-oss

Il vous suffit ensuite de redémarrer Firefox pour que le son passe à marcher !



Je remercie Paulo de Tarso [4] pour ce conseil !

Références :

[1] VLC open-source multimedia framework, player and server

[2] ALSA: the Advanced Linux Sound Architecture

[3] OSS is the free version of the Open Sound System

[4] 70015: A vida com Linux, Mac, Windows e Iphone


sexta-feira, 19 de junho de 2009

Testando HTTP sem usar um navegador



Um servidor WEB funciona utilizando o protocolo HTTP [1], e responde a requisições tais como "HTTP GET" vindas de um cliente (o browser).

Geralmente não vemos o que está sendo transmitido entre as duas partes: cliente (navegador) e servidor WEB. Entretanto, podemos facilmente inspecionar esse tráfego de dados através da ferramenta telnet [2], disponível em praticamente qualquer plataforma.

Para fazer o teste, primeiro abra um terminal no Linux ou um prompt de comando no Windows. Você vai precisar então digitar o comando com a seguinte sintaxe:

telnet [servidor] [porta]

Por exemplo, para acessar a página do Grupo de Usuários PostgreSQL do Brasil [3], execute o seguinte:

telnet postgresql.org.br 80

Com isso você estará conectado ao servidor WEB especificado e poderá desta forma executar qualquer comando do protocolo HTTP [4] desejado, tal como GET, HEAD ou POST.

Em seguida, para efetuar uma requisição HTTP do tipo GET ao servidor, você precisará executar um comando no seguinte formato:

GET [página] HTTP/1.0

Para exemplificar, faça uma simulação à esta página, digitando o texto a seguir no telnet:

GET /node/61 HTTP/1.0

Atenção: pressione ENTER duas vezes. Se a página existir no servidor, você terá como resultado algo como ilustrado na figura abaixo.




Preste atenção no cabeçalho (header) HTTP enviado pelo servidor após a execução. São justamente estes dados que o browser interpreta automaticamente e o usuário não vê.


HTTP/1.1 200 OK
Date: Sun, 21 Jun 2009 02:08:33 GMT
Server: Apache/2.2.9 (Debian) PHP/5.2.0-8+etch15 mod_ssl/2.2.9 OpenSSL/0.9.8g
X-Powered-By: PHP/5.2.0-8+etch15
Set-Cookie: SESSd41d8cd98f00b204e9800998ecf8427e=baefb8cb6bc0a5ed29340ec05aa6ff38; expires=Tue, 14 Jul 2009 05:41:54 GMT; path=/
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Last-Modified: Sun, 21 Jun 2009 02:08:34 GMT
Cache-Control: store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0
Connection: close
Content-Type: text/html; charset=utf-8


A primeira linha é uma das mais importantes do cabeçalho, pois ela indica se houve sucesso ou erro durante o atendimento da requisição HTTP. O código 200 no exemplo significa que a página HTML foi recuperada com sucesso.

Você pode ainda receber um código de status 404 caso a página não seja encontrada (ou seja, o famoso "Erro 404"), o código 301 se a página tiver sido movida permanentemente ou 401 caso você não tenha autorização para acessá-la.

A lista completa dos códigos de status do protocolo HTTP pode ser consultada aqui [5]. Se você é um administrador de sistemas ou desenvolvedor de aplicações WEB possivelmente esta abordagem lhe será útil. :D

O conteúdo HTML que será renderizado pelo browser vem logo após o cabeçalho.

Referências:
[1] http://pt.wikipedia.org/wiki/Http
[2] http://pt.wikipedia.org/wiki/Telnet
[3] http://postgresql.org.br
[4] Hypertext Transfer Protocol -- HTTP/1.0
[5] RFC 2616 - Status Code Definitions

domingo, 14 de junho de 2009

Sessão nostalgia: Microsoft Windows 3.1



Ano: 1993. O sistema operacional geralmente encontrado em computadores pessoais aqui era o MS-DOS (versão 5.0, no máximo 6.22!). O Microsoft Windows 3.1, que era opcional na época, não passava de um gerenciador de aplicativos, o "Program Manager". O Linux? Esse não devia ter 2 anos ainda e ninguém fora do meio acadêmico ou talvez longe da Escandinávia tinha conhecimento dele...


O tempo passou e aquele simples agregador de janelinhas foi tomando espaço e quem passou a ser esquecido foi o pobre do MS-DOS, que nos dias atuais não passa de um "Prompt de comando". Ninguém mais ouviu falar de AUTOEXEC.BAT, HIMEM.SYS, GORILLA.BAS, MEMMAKER, QEMM, DOS4GW...


E eis que essa semana vejo um artigo na Internet em que um polonês maluco havia instalado o Microsoft Windows 3.1 no Symbian [1]...! Resolvi seguir o tutorial e fazer o mesmo, só por brincadeira, e funcionou mesmo! Aliás, não serve para nada além disso, pois fica lento, ruim de visualizar na tela, e sem teclado alfanumérico...


Mesmo assim, empolgado pura e simplesmente pelo fator nostálgico, resolvi testar tal inutilidade janélica também no Linux! No caso do N95, o pseudo sistema operacional (AKA Win3.1) estava rodando em cima de um ambiente controlado pelo do DOSBox [2]. Na realidade o maior trabalho nessa façanha foi o porte do DOSBox para o Symbian pelo tal "kolijoco".


No Linux, basta instalar o pacote dosbox. Em distros Debian-like, podemos fazer isso através do comando:

# apt-get install dosbox


Os discos de instalação do Windows 3.1 (na realidade 7 disquetes de 1,44 MB) podemos baixar de fontes extra-oficiais, como sites 4shared ou torrents. Acho que ninguém vai se importar de piratearmos um software que já está debutando, né?


Abra um terminal, dirija-se ao diretório em que o Windows 3.1 será instalado e execute o DOSBox:

$ cd /opt/win31
$ dosbox


Em seguida, já dentro do DOSBox, execute alguns comandos para montar o disco rígido e o primeiro disquete de instalação:

mount c .
mount a /tmp/win31setup


Neste exemplo, os drives serão mapeados no DOS conforme o esquema a seguir:
  • C: diretório /opt/win31 no Linux;
  • A: diretório /tmp/win31setup no Linux.

Execute a instalação do Windows através do comando a seguir:

a:\setup.exe


Então basta seguir um clássico "Next, Next, Finish" e ao final executar:

c:
cd \windows
win


No final, eis o resultado: o Microsoft Windows 3.1 sendo executado em uma janelinha de 640x480 pixels no Linux!



Para ter uma idéia do que está acontecendo, veja a tela inteira do Ubuntu Linux, rodando o Gnome como gerenciador de desktop e o DOSBOX ali no canto, totalmente sob controle...




Para facilitar as execuções posteriores, crie um arquivo com o nome "dosbox.conf" dentro do diretório do Windows 3.1 com o seguinte conteúdo:

[sdl]
# sem isso as setas do teclado não funcionam...
usescancodes=false

[autoexec]
# meu teclado é layout alemão, substitua "gr" por "br"
keyb gr
# o resto aqui é para executar o windows ao iniciar
mount c .
c:
cd windows
win

Agora, isso pode parecer piada nos tempos atuais: o Windows 3.1 recém-instalado não chegava a ocupar 12 MB de espaço em disco! Quem imaginaria que um dia o Windows iria exigir 10 GB de HD, hein!




O mais surpreendente é pensar que conseguíamos ser felizes com informática mesmo sem a World Wide Web... Agora vou procurar outras relíquias computacionais... Será que acho o Wolfenstein 3D ou Microsoft Word 6..? :D


Referências:
[1] Windows 3.1 Runs On a Nokia N95, Creating Dangerous Ripple in Space-Time
[2] DOSBox: a x86 emulator with DOS

quarta-feira, 20 de maio de 2009

Echoing hidden psql statements



One of the greatest advantages of a Database Management System is its embedded data dictionary, also called metadata or system catalog [1]. On PostgreSQL DBMS it is not different, and its metadata are widely used by database handling tools.

A great and yet simple tool available to this relational database is psql [2], a command-line, bash-enabled application that permits issuing SQL instructions to PostgreSQL server instances.

Even when sending a simple command such as "\d" on psql, there might be one or several SQL instructions sent internally to that given instance. Knowing exactly what is being sent could help a database administrator or even a simple developer on common and ordinary daily tasks.

So, how could we know those SQL instructions? Simpler than expected!

There is an environment variable on psql called ECHO_HIDDEN, which once set echoes all hidden instructions, as its own name says. In order to enable that, simply execute the following command:


# \set ECHO_HIDDEN

Then, try to issue some internal commands like "\l" (typing "\?" shows the entire list of options). Here is the output after enabling ECHO_HIDDEN:


# \l
********* QUERY **********
SELECT d.datname as "Name",
r.rolname as "Owner",
pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding"
FROM pg_catalog.pg_database d
JOIN pg_catalog.pg_roles r ON d.datdba = r.oid
ORDER BY 1;
**************************

List of databases
Name | Owner | Encoding
-------------+----------------+----------
auction5 | sa_auction5 | UTF8
postgres | postgres | UTF8
rodrigo | rodrigo | UTF8
template0 | postgres | UTF8
template1 | postgres | UTF8
(5 rows)

Note the contents enclosed by "QUERY" and "***". Here are the internal SQL instructions respectively performed.

When you're done, you could deactivate this variable by executing the command below:


# \unset ECHO_HIDDEN

That's it! :D

References:
[1] PostgreSQL 8.3.8 Documentation - System Catalogs
[2] psql - PostgreSQL interactive terminal

quarta-feira, 25 de março de 2009

Meu PostgreSQL não conecta!


Começando do início...


Observação: Apesar de os testes terem sido feitos em ambiente Linux, os comandos (ping, telnet, psql) e arquivos de configuração (postgresql.conf, pg_hba.conf) existem e funcionam também no Windows, Macintosh ou FreeBSD, no respectivo terminal.

Antes de tudo, façamos alguns testes que respondem à algumas perguntas.

A máquina está no ar e é enxergada na rede pelo cliente?


Um simples "ping" pode nos ajudar:
$ ping 10.15.23.15
Se estiver tudo correto, aparecerá o texto abaixo:
rodrigo@asgard:~$ ping 10.15.23.15
PING 10.15.23.15 (10.15.23.15) 56(84) bytes of data.
64 bytes from 10.15.23.15: icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from 10.15.23.15: icmp_seq=2 ttl=64 time=0.034 ms
64 bytes from 10.15.23.15: icmp_seq=3 ttl=64 time=0.034 ms

--- 10.15.23.15 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.034/0.037/0.044/0.006 ms
Caso contrário, será exibido algo como:
rodrigo@asgard:~$ ping 10.15.23.150
PING 10.15.23.150 (10.15.23.150) 56(84) bytes of data.
From 10.15.23.15 icmp_seq=1 Destination Host Unreachable
From 10.15.23.15 icmp_seq=2 Destination Host Unreachable
From 10.15.23.15 icmp_seq=3 Destination Host Unreachable

--- 10.15.23.150 ping statistics ---
6 packets transmitted, 0 received, +3 errors,
100% packet loss, time 5008ms, pipe 3
Se este for o caso, resolva este problema de conexão e configuração de rede antes de continuar.

O servidor está respondendo ao serviço na porta do PostgreSQL?


Se não estiver, qualquer acesso externo ao PostgreSQL é barrado, e a seguinte mensagem será exibida:
psql: could not connect to server: Conexão recusada
Is the server running on host "10.15.23.15" and accepting
TCP/IP connections on port 5432?
Para comprovar isso, podemos fazer um simples teste com o "telnet":
$ telnet 10.15.23.15 5432
rodrigo@asgard:~$ telnet 10.15.23.15 5432
Trying 10.15.23.15...
telnet: Unable to connect to remote host: Connection refused
Este é um problema que ocorre com 5 de cada 4 iniciantes neste banco de dados: a conexão não-local!
Bom, o fato é que a configuração padrão do PostgreSQL faz com que apenas conexões locais (via soquete UNIX) sejam permitidas. O primeiro passo é alterar uma opção no arquivo de configurações postgresql.conf:















VersãoOriginalMudar para
7.X e anteriorestcpip_socket = falsetcpip_socket = true
8.X em diantelisten_addresses = 'localhost'listen_addresses = '*'

Após salvar o arquivo, será preciso reiniciar o SGBD (não basta apenas fazer um "reload").

Agora refaça o teste do "telnet". Terá que aceitar a conexão e aparecer este texto:
rodrigo@asgard:~$ telnet 10.15.23.15 5432
Trying 10.15.23.15...
Connected to 10.15.23.15.
Escape character is '^]'.
Dê um CTRL+C para sair. Se ainda não funcionar, será preciso verificar se firewalls não estão impedindo a conexão entre o cliente e o servidor, na porta do PostgreSQL (padrão: 5432). Resolva essa questão antes de continuar a leitura...

Opa! Metade do serviço está concluída! Agora, outro problema que atormenta quem está começando, este erro ao tentar se conectar:
$ psql -h 10.15.23.15 correios rodrigo
psql: FATAL:  nenhuma entrada no pg_hba.conf para máquina "10.15.22.32",
usuário "rodrigo", banco de dados "correios", SSL desabilitado
Sigamos a dica que o PostgreSQL nos dá! Abra o seguinte arquivo de configuração: pg_hba.conf.

Este arquivo controla: quais hosts têm permissão de conexão, como os clientes se autenticam, quais usuários do PostgreSQL podem ser usados e que bancos de dados eles podem acessar. Os registros podem ter uma das seguintes formas:

local      DATABASE  USER  METHOD  [OPTION]
host DATABASE USER CIDR-ADDRESS METHOD [OPTION]
hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTION]
hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTION]
Sendo assim, nestas entradas de permissões de acesso ao PostgreSQL, podemos alterar:
  • tipo de conexão ("local", "host")
  • banco de dados ("all": todos)
  • usuário ("all": todos)
  • endereço IP e máscara (estilo CIDR)
  • método ("reject", "trust", "password", "md5", "ident same user")

Importante: o arquivo é lido de cima para baixo, e a primeira entrada que esteja de acordo com a requisição é considerada. Isso é um fato que às vezes confunde os administradores. Os campos podem ser separados por espaços ou tabulações - tanto faz, funcionará de ambas as formas.

Bom, para resolver o problema em questão, precisamos incluir a seguinte linha:

host    correios    rodrigo        10.15.22.32/32        md5
Desta vez, não será preciso reiniciar o PostgreSQL. No Linux, podemos enviar um sinal do tipo HUP das seguintes maneiras:
$ /etc/init.d/postgresql-8.1 reload
$ killall -HUP postmaster
No Windows eu vou ficar devendo, mas deve ser algo como "recarregar o serviço".

Pronto! Simples, não?

Se você quiser, pode fazer com que o PostgreSQL funcione de modo promíscuo, adicionando a seguinte linha:

host    all    all        0.0.0.0/0        trust
Isso faz com que qualquer usuário, de qualquer IP acesse qualquer banco de dados, e sem necessidade de senha!

Se, ao invés de "trust" for usado "reject", todo acesso será fechado.

Lembre-se que, pelo padrão CIDR de endereçamento, diversos IPs podem ser
configurados com uma só linha! Por exemplo, ambas as linhas abaixo fazem
com que toda a subrede do IP 10.15.22.32/22 tenha acesso, por senha, ao
banco "correios":

host    correios    all        10.15.20.0/22        password
host correios all 10.15.22.32 255.255.252.0 password

Uma coisa interessante é restringir o acesso ao usuário "postgres", Senhor de Todo o Cluster, com a inclusão da seguinte linha:

local   all         postgres                          ident sameuser
Com isso, somente acesso local (via "ssh" e instrução "su - postgres") poderá ser feita com este super usuário, que tem permissão para fazer o que quiser em qualquer banco de dados.

sábado, 7 de março de 2009

Setting up Hamachi on Debian GNU/Linux


"LogMeIn Hamachi [1] is a VPN service that easily sets up in 10 minutes, and enables secure remote access to your business network, anywhere there's an Internet connection."

Hamachi is indeed a great tool for easily setting up VPNs! It creates a virtual network interface and all its configurations are made up with almost no need of user intervention.

For Windows users, a "Next-Next-Finish"-like setup executable is provided. Unix/Linux users, however, have no such facilities and should burn some neurons before putting it to work.

In this article I'll show some tips on how to build up the whole scenario on Debian GNU/Linux operating system, including installing Hamachi binaries, setting up required libraries, and deploying automatic initialization scripts.

First of all, you should download Hamachi binaries for Linux [2]. In the given URL, retrieve the most suitable binary for your processor (i.e. choose between an Intel Pentium or "others"). At the time of writing this very document, the current Hamachi release was 0.9.9.9-20.

For instance, I downloaded hamachi-0.9.9.9-20-lnx.tar.gz as my CPU was an AMD Sempron. I'll use this file name as the example from now on. Save the file on a proper Linux directory, say /usr/src.

Then, get into that destiny directory and extract all the zipped file contents using the following commands:

$ cd /usr/src
$ tar xvzf hamachi-0.9.9.9-20-lnx.tar.gz

Convention: Please observe that in the given notation the prefix "$" means a normal user prompt is needed, whereas a "#" prefix needs a root or superuser terminal to input the commands. Also, texts colored in red are user inputs. Its corresponding outputs are styled in blue.

You should note there is now a sub-directory called hamachi-0.9.9.9-20-lnx. Get into it and then switch to a super user account (i.e. "root" or any supercow-powered user).

$ cd hamachi-0.9.9.9-20-lnx
$ su

Although Hamachi can be set per user, in this case I'll set it up for the entire system, I mean, any user can make use of its networking services, and it will be configured for the "hamachi" account (to be created).

# make install

You should expect some output like this:

Copying hamachi into /usr/bin ..
Creating hamachi-init symlink ..
Compiling tuncfg ..
Copying tuncfg into /sbin ..

Hamachi is installed. See README for what to do next.

Perhaps you get stuck into dependencies, like OpenSSH or OpenSSL. If that is the case, install the required packages (e.g. by calling "apt-get install openssl") before proceeding and then try installing hamachi again. A nice try is to use ldd command onto "hamachi" or "tuncfg" binaries in order to have a clue of what file or package needs to be resolved.

Next, run the TUN/TAP device driver [3] configurator:

# ./tuncfg/tuncfg

This time if nothing comes out everything is alright. :)

The next step consists in generating the user crypto identity:

# hamachi-init

You will see the following output in the terminal:

Initializing Hamachi configuration (/root/.hamachi).
Please wait ..
generating 2048-bit RSA keypair .. ok
making /root/.hamachi directory .. ok

saving /root/.hamachi/client.pub .. ok

saving /root/.hamachi/client.pri .. ok

saving /root/.hamachi/state .. ok

Authentication information has been created.

Hamachi can now be started with
'hamachi start' command and then brought online with 'hamachi login'.

Voilà, all the required security keys (private and public) have just been generated on a hidden directory called .hamachi inside the user's home. Note that it is a highly encrypted 2048-bit RSA keypair!

Initialize manually the service by calling the instruction below:

# hamachi start

This single line should appear:

Starting Hamachi hamachi-lnx-0.9.9.9-20 .. ok

Next, it is desirable for you to set a Hamachi nick for the current client by typing:

# hamachi set-nick zangaro

In your case, replace "zangaro" by, say, the identification you usually give your computer.

Now you should put the daemon online and create an account by running this command:

# hamachi login

This simple message is expected:

Logging in ......... ok

If you have no network yet to join, you will need to create yours by typing:

# hamachi create Agajorte

Replace "Agajorte" by the name you will give your new network. A password will be prompted and then the network will be created.

If you are going to join an existing network, just type:

# hamachi join Agajorte

Then, to appear to other users, type this command:

# hamachi go-online Agajorte

Now, to list other members in the network and their respective status, type:

# hamachi list

By default peers' nicknames are not shown in the listing. In order to enable it, you will need to run this command:

# hamachi get-nicks

If you type "hamachi" without any arguments, the outcome is something like this:

Hamachi, a zero-config virtual private networking utility, ver 0.9.9.9-20

version : hamachi-lnx-0.9.9.9-20
pid : 5063
status : logged in
nickname : zangaro

Also, you could have Hamachi usage tips shown by running this command:

# hamachi help

If you successfully came so far, congratulations! We're half way to conclude the process...

You will now need to stop the daemon. Run this command:

# hamachi stop

As every secondary service in the UNIX world, Hamachi daemon could not be initialized using the superuser root account for security reasons. A so-called system user for this service will be created for Hamachi administration tasks. Type the following instruction to add an user called "hamachi" to /etc/passwd.

# adduser --system --disabled-password --no-create-home hamachi

Adding system user `hamachi' (UID 108) ...
Adding new user `hamachi' (UID 108) with group `nogroup' ...
Not creating home directory `/home/hamachi'.
zangaro:/home/rodrigo# id hamachi
uid=108(hamachi) gid=65534(nogroup) grupos=65534(nogroup)

Note that this user has no home directory. He won't need it.

Then, move the Hamachi first initialized configuration directory to /etc/hamachi. In order to let the superuser to still execute Hamachi operations, a symbolic link will be created. Finally, change /etc/hamachi directory and respective ownership to the newly added "hamachi" account. Here are the referenced commands:

# mv /root/.hamachi /etc/hamachi
# ln -s /etc/hamachi /root/.hamachi
# chown hamachi.hamachi /etc/hamachi/ -R

The next step is to develop a Shell Script to start and stop Hamachi daemon. Run this command:

# vi /etc/init.d/hamachi

Then type the instructions below:



#!/bin/bash
#
# hamachi This shell script takes care of starting and stopping hamachi.
# author: Rodrigo HJORT (http://agajorte.blogspot.com)
#
# chkconfig: 345 99 9
# description: hamachi is a zero-configuration VPN
#

PATH=/sbin:/bin:/usr/bin

HAMUSR=hamachi
HAMDIR=/etc/hamachi
HAMBIN=/usr/bin/hamachi

. /lib/lsb/init-functions

[ -f $HAMDIR/client.pri ] || exit 2
[ -f $HAMDIR/client.pub ] || exit 3

[ -f $HAMBIN ] || exit 4

do_start () {
echo "Starting hamachi..."
/sbin/tuncfg
call_daemon start
}

do_status () {
call_daemon
}

do_stop () {
echo "Stopping hamachi..."
killall tuncfg
call_daemon stop
}

call_daemon () {
su $HAMUSR -c "$HAMBIN -c $HAMDIR $1"
}

case "$1" in
start)
do_start
;;
stop)
do_stop
;;
restart)
do_stop
sleep 1
do_start
;;
status)
do_status
;;
*)
echo "Usage: hamachi {start|stop|restart|status}" >&2
exit 1
;;
esac

Give proper execution permissions to the script file by running the following command:

# chmod +x /etc/init.d/hamachi

Now you could perform a simple test by calling:

# /etc/init.d/hamachi

Usage: hamachi {start|stop|restart|status}

Then start the daemon:

# /etc/init.d/hamachi start

Starting hamachi...
Starting Hamachi hamachi-lnx-0.9.9.9-20 .. ok

And then retrieve the service status:

# /etc/init.d/hamachi status

Hamachi, a zero-config virtual private networking utility, ver 0.9.9.9-20

version : hamachi-lnx-0.9.9.9-20
pid : 4997
status : logged in
nickname : zangaro-lin

The most important fact to observe now is that Hamachi service is no longer bound to "root" superuser, but to its proper account: the "hamachi" system user.

# ps aux | grep ^hamachi

hamachi 5063 0.1 0.0 3104 804 ? S 01:42 0:00 /usr/bin/hamachi -c /etc/hamachi start

You could also restart the service by invoking this command:

# /etc/init.d/hamachi restart

Stopping hamachi...
Shutting down .. ok
Starting hamachi...
Starting Hamachi hamachi-lnx-0.9.9.9-20 .. ok

At last, you could stop the service by calling this:

# /etc/init.d/hamachi stop

Stopping hamachi...
Shutting down .. ok

Hold on, there is only one detail left to do!

It will be very interesting to have the service initialized automatically as the system starts, i.e. on Linux boot time. This is a responsibility for System-V, but usually its configuration is distribution-dependent. You should check out your Linux distro on how to do it.

As Debian environment was chosen for the tests, System-V style init script links were installed through update-rc.d command, as shown below:

# update-rc.d hamachi defaults 09 99

Adding system startup for /etc/init.d/hamachi ...
/etc/rc0.d/K99hamachi -> ../init.d/hamachi
/etc/rc1.d/K99hamachi -> ../init.d/hamachi
/etc/rc6.d/K99hamachi -> ../init.d/hamachi
/etc/rc2.d/S09hamachi -> ../init.d/hamachi
/etc/rc3.d/S09hamachi -> ../init.d/hamachi
/etc/rc4.d/S09hamachi -> ../init.d/hamachi
/etc/rc5.d/S09hamachi -> ../init.d/hamachi

Well, unless you care about your uptime or if you are sure about the configuration you made on Sytem-V, you might reboot your Linux to find out whether the entire effort was worthy. Bon courage !

Even if you have another kind of Linux, I hope most of information detailed in the present document are valuable for you. Please don't hesitate in scratching up some comments. :D

References:

[1] Hamachi Official Site, https://secure.logmein.com/products/hamachi/vpn.asp
[2] Hamachi for Linux Binaries, http://files.hamachi.cc/linux/
[3] TUN/TAP device driver, http://hamachi.cc/tuntap