Java + Spring + LDAP + Active Directory Windows

dezembro 14, 2011

Por diversos momentos na vida de arquitetos/desenvolvedores de software recebemos o bendito requisito vindo do usuário de que o seu sistema web precisa desesperadamente conversar com o Microsoft Active Directory. Desde que me conheço no desenvolvimento vejo isso acontecendo e vejo diversos desenvolvedores sentindo um frio na espinha neste momento.

Creio que tenha sido com o intuíto de ajudar estes pobres é que os geniais lá do Spring resolveram quebrar mais uma. É o Spring LDAP.

Não vou colocar o projeto inteiro aqui, o foco deste post não é ensinar a criar um projeto jsf + spring, mas vou tentar detalhar legal a parte do ldap que tanto nos aflige.

Inicialmente vou colocar o meu pom.xml com todas as dependências, creio que este arquivo seja o mais importante e ao mesmo tempo o mais problematico.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.app</groupId>
<artifactId>App</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>1.2_08</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>1.2_08</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.facelets</groupId>
<artifactId>jsf-facelets</artifactId>
<version>1.1.15</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-ui</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.5.RELEASE</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.3.2</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.1_3</version>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-faces</artifactId>
<version>2.3.0.RELEASE</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>activation</artifactId>
<groupId>javax.activation</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.4.0.GA</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.snowdrop</groupId>
<artifactId>snowdrop-vfs</artifactId>
<version>1.0.1.GA</version>
</dependency>
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>1.3</version>
<exclusions>
<exclusion>
<artifactId>activation</artifactId>
<groupId>javax.activation</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws</artifactId>
<version>1.5.8</version>
<classifier>all</classifier>
</dependency>

<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.0</version>
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rmi</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jboss</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<directory>${basedir}/target</directory>
<outputDirectory>${basedir}/target/classes</outputDirectory>
<testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<scriptSourceDirectory>${basedir}/src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>${basedir}/src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<executable>${env.JAVA_HOME}/bin/javac</executable>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
<optimize>true</optimize>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.6</version>
</plugin>
</plugins>
</build>
</project>

Por cima você já consegue ver que é um projeto JSF 1.2, Spring e um pouco de Hibernate, como eu disse, o que não se aplica a sua aplicação deve ser simplesmente ignorado. Tomei a decisão de colocar todo o pom aqui, pois é muito comum ficarmos presos no famoso dependency-hell do Java, mas a única dependência que de fato faz a diferença neste post é spring-security-ldap.

A primeira coisa é criar o bean de ldapTemplate dentro do seu contexto do Spring, é através deste bean que você se comunicará com o AD e ele é a principal figura deste projeto.

Vamos lá:


<bean id="properties">
<property name="locations">
<list>
<value>/WEB-INF/classes/com/app/app/resources/ldap.properties</value>
</list>
</property>
</bean>

<bean id="contextSource">
<property name="url" value="${ldap.url}" />
<property name="userDn" value="${ldap.userName}" />
<property name="password" value="${ldap.password}" />
<property name="pooled" value="${ldap.pooled}"/>
<property name="referral" value="${ldap.referral}"/>
</bean>

<bean id="ldapTemplate">
<constructor-arg ref="contextSource" />
<property name="ignorePartialResultException" value="${ldap.ignore}"/>
</bean>

Nas linhas de código acima criarmos três beans bem simplezinhos. O primeiro properties, apenas carregamos o arquivo especificado (o seu arquivo pode estar em qualquer lugar, ou mesmo não existir) para que possamos parametrizar os valores do servidor ldap de fora do arquivo xml do Spring.

Olha o meu arquivo properties aí:


ldap.url=ldap://dominio.com.br/DC=dominio,DC=com,DC=br
ldap.userName=leonardo.moreira@dominio.com.br
ldap.password=senha
ldap.userPrincipalPath=(userPrincipalName={0})
ldap.pooled=true
ldap.referral=follow
ldap.ignore=true

Vou frisar que o endereço da propriedade ldap.url varia de acordo com suas configurações de ldap. Veja, em geral é sempre o nome do seu domínio seguido pelos sufixos definidos pela galera da sua Infra-estrutura.
Uma dica para ver alguns destes dados é aquela busca avançada de usuários do windows, sabe? Você pode adicionar diversos campos e valores que constituem o domínio e outras informações úteis.

Uma vez com o properties em mãos o aplicamos ao conector do ldap, no caso o bean contextSource. Este bean irá criar a comunicação com o ldap e lhe passar o contexto do mesmo para que você trabalhe livremente utilizando o ldapTemplate. Falando em ldapTemplate o criamos no bean seguinte passando o contextSource no construtor.

Bom, uma vez que nossa aplicação esteja subindo no web-server com os beans descritos acima vem a parte mais fácil que é lidar com o contexto do Ldap.

Aqui na minha aplicação eu criei um serviço simples que apenas faz a busca dos usuários no AD com o critério LIKE (Do SQL lembra? :) )

Bom, a primeira coisa de tudo aqui é definir as nossas interfaces, afinal, desacoplamento nunca é ruim, correto?

A interface User descreve os campos que a nossa classe concreta de usuário deverá ter. Esta interface é bem pessoal, tem gente que gosta de criar e tem gente que não vê a necessidade. Em todo caso está aí.



package com.app.app.ldap;

/**
* @author Leonardo Machado Moreira
* @version 1.0 04/11/2011
*/
public interface User {

public String getUserName();

public void setUserName(String userName);

public String getFirstName();

public void setFirstName(String firstName);

public String getLastName();

public void setLastName(String lastName);

public String getEmail();

public void setEmail(String email);

public String getPassword();

public void setPassword(String password);

public String getDepartment();

public void setDepartment(String departement);

public String getLogin();

public void setLogin(String login);

public String [] getGroups();

public void setGroups(String [] groups);
}

A interface User será implementada pela classe DefaultUser, que terá o papel de mapear de fato um usuário do ldap.


/*
* @(#)DefaultUser.java 1.0 04/11/2011
*
* Copyright (c) 2011, GSW Software Ltda. Todos os direitos reservados.
* Propriedade particular/confidencial da GSW. Uso sujeito a termos de licença.
*/

package com.app.app.ldap;

/**
* @author Leonardo Machado Moreira
* @version 1.0 04/11/2011
*/
public class DefaultUser implements User {

private String userName;
private String firstName;
private String lastName;
private String email;
private String password;
private String department;
private String login;
private String groups[];

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getDepartment() {
return department;
}

public void setDepartment(String department) {
this.department = department;
}

public String [] getGroups() {
return groups;
}

public void setGroups(String [] groups) {
this.groups = groups;
}

@Override
public String getLogin() {
return login;
}

@Override
public void setLogin(String login) {
this.login = login;
}

public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("UserImpl[");
buffer.append(" userName = ").append(userName);
buffer.append(" email = ").append(email);
buffer.append(" firstName = ").append(firstName);
buffer.append(" lastName = ").append(lastName);
buffer.append(" password = ").append(password);
buffer.append("]");
return buffer.toString();
}

}

Agora mapearemos o serviço em sí, e lá vai mais uma interface.

Segue:


package com.app.app.ldap;

import java.util.List;

/**
* @author Leonardo Machado Moreira
* @version 1.0 04/11/2011
*/
public interface UserService {

User getUser(final String userName);

List<User> getUsers(final String pattern);

}

Dois métodos simples de tudo, um retorna um usuário específico através da busca pelo nome e o outro retornará uma lista de usuários a partir de uma String qualquer, onde será feito o like.



package com.app.app.ldap;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.LikeFilter;
import org.springframework.ldap.filter.OrFilter;
import org.springframework.stereotype.Service;

/**
* @author Leonardo Machado Moreira
* @version 1.0 04/11/2011
*/
@Service
public class DefaultUserService implements UserService {

@Autowired
private LdapTemplate ldapTemplate;

public User getUser(final String userName) {
OrFilter filter = new OrFilter();
filter.or(new EqualsFilter("givenName", userName));

@SuppressWarnings("unchecked")
List<User> users = getLdapTemplate().search(DistinguishedName.EMPTY_PATH, filter.encode(), new UserAttributesMapper());

if (!users.isEmpty()) {
return users.get(0);
}

return null;
}

@SuppressWarnings("unchecked")
public List<User> getUsers(final String pattern) {

OrFilter filter = new OrFilter();

if (pattern != null) {
filter.or(new LikeFilter("givenName", pattern));
}

List<User> users = new ArrayList<User>();

users.addAll(getLdapTemplate().search(DistinguishedName.EMPTY_PATH, filter.encode(), new UserAttributesMapper()));
return users;

}

public LdapTemplate getLdapTemplate() {
return ldapTemplate;
}

}

É na classe acima que toda a mágica acontece. Ela é que será chamada nos outros locais da sua aplicação (controladores) e ela que encapsulará toda a complexidade da comunicação com o AD.

A classe é tão simples e tão descritiva que tenho até preguiça de explicar.

Fiz o Autowired do ldapTemplate que nós criamos lá no XML um pouco para trás e então é só utiliza-lo a-la-vonte.

A linha mais importante desta classe é obviamente a linha a seguir.

List<User> users = getLdapTemplate().search(DistinguishedName.EMPTY_PATH, filter.encode(), new UserAttributesMapper());

Aqui listamos o filtro e a classe de mapeamento onde a string retornada pelo ldap será convertida na nossa entidade.

Vejam:


public class UserAttributesMapper implements AttributesMapper {

@Override
public Object mapFromAttributes(Attributes attrs) throws NamingException {
DefaultUser user = new DefaultUser();

if (attrs.get("cn") != null) {
user.setUserName((String) attrs.get("cn").get());
}

if (attrs.get("cn") != null)
user.setFirstName((String) attrs.get("cn").get());

if (attrs.get("sn") != null)
user.setLastName((String) attrs.get("sn").get());

if (attrs.get("mail") != null)
user.setEmail((String) attrs.get("mail").get());

if (attrs.get("sAMAccountName") != null)
user.setLogin((String) attrs.get("sAMAccountName").get());

return user;
}

}

É importante ressaltar que não fui eu que inventei estas constantes (sn, cn e etc), também as abomino, mas é a forma que a Microsoft gerencia os dados de seus usuáriso no AD.

Com uma googlada simples eu encontrei o descritivo de todos os atributos, tá no link:

http://www.computerperformance.co.uk/Logon/LDAP_attributes_active_directory.htm

Bom, creio que agora temos 99% do trabalho pronto, vou só mostrar como ficou a chamada lá no controller do meu jsf, onde eu populo um autocomplete o Richfaces, conforme o usuário vai digitando eu vou listando todos os usuários do AD a partir do nome.


@Autowired
private UserService defaultUserService;

public List<com.embraer.icm.ldap.User> autocomplete(Object suggest) {
try {
List<com.embraer.icm.ldap.User> users = new ArrayList<com.embraer.icm.ldap.User>();
users.addAll(defaultUserService.getUsers(suggest + "*"));
return users;
} catch (Exception e) {
return new ArrayList<com.embraer.icm.ldap.User>();
}
}


<h:inputText id="name" value="#{userController.entity.name}"
required="true"
requiredMessage="#{labels['user.labels.required.name']}"
disabled="#{userController.rendered}" style="width:300px;" />

<rich:suggestionbox for="name" var="result"
suggestionAction="#{userController.autocomplete}">
<h:column>
<h:outputText value="#{result.userName}" />
</h:column>
<a4j:support event="onselect" reRender="login, email">
<f:setPropertyActionListener value="#{result.email}"
target="#{userController.entity.email}" />
<f:setPropertyActionListener value="#{result.login}"
target="#{userController.entity.login}" />
</a4j:support>
</rich:suggestionbox>

Pronto, agora sim, tudo que precisamos para nos comunicar com o LDAP e ainda mostrar em um auto complete do Richfaces. É óbvio que não coloquei as configurações básicas de framework, afinal, perderia toda a graça de programar, não é?

Mas quem realmente tiver dificuldade em uma configuração simples de JSF + Spring e etc, pode comentar ai que eu dou uma força, mas dê uma boa googlada antes, tá?

Ah, outra coisa que esqueci de comentar, o ldapTemplate não faz apenas pesquisa, ele chega ao cúmulo de modificar usuários e até cria-los dentro do AD, é só questão de brincar melhor com a sua API e com os seus atributos.

É isso aí, até a próxima.

Leonardo

Java RMI fácil utilizando o Spring – Cliente

julho 16, 2011

Esta é a continuação do post anterior.

Uma vez que já criamos o servidor RMI basta consumi-lo. Vou mostrar de duas formas para ficar bem entendido.

A primeira é já na sua aplicação web, maneira mais simples de todas. No seu application context .xml ou arquivo que o valha adicione o seguinte bean:


<bean id="rmiService">
<property name="serviceUrl" value="rmi://192.168.224.75:1099/rmi-service"/>
<property name="serviceInterface" value="com.app.sys.rmiclient.FileManagement"/>
</bean>

Neste bean podemos notar algums coisinhas simples. Ele cria um bean chamado rmiService e configura-o para o endereço onde o nosso servidor RMI está rodando, no meu caso é
este IP configurado aí.

Este codigo especifica uma interface também, para o Spring saber exatamente o que está criando. Note que nossa interface é exatamente a mesma interface que criamos no servidor RMI. DEVE ser a mesma interface
se puder copiar e colar melhor ainda.

Creio que tenha ficado bem claro que nosso cliente deve ter uma versão da Interface do RMI, né?

Agora que o bean está no seu application context, sempre que sua aplicação “subir” vai tentar fazer a conexão e se o RMI não estiver rodando no servidor vai retornar um erro.

Quando a aplicação subir é só fazer o @Autowired utilizando o rmiService do tipo FileManagement e usar os métodos da interface.

Simples, simples.

Para quem quer um pouco mais de detalhes eu criei um outro JAR chamado client-rmi, o contrário do server-rmi que criei no post anterior, mas volta lá e pegue a definição do POM
e pegue também a estrutura de pastas do resources por exemplo.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="rmiService">
<property name="serviceUrl" value="rmi://192.168.224.75:1099/rmi-service"/>
<property name="serviceInterface" value="com.app.sys.rmiclient.FileManagement"/>
</bean>

</beans>


Veja que a criação do bean aqui é exatamente a mesma que usamos no nosso container web, isto mostra legal o quão parrudo o Spring é para criar o seu próprio contexto.

Como já descrevi você cola a interface FileManagement no seu client.

E agora o simples e fácil Main.



import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Scanner;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
public static void main(String[] args) throws IOException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/app/sys/rmiclient/rmi-server-context.xml");
FileManagement management = (FileManagement) ctx.getBean("rmiService");

System.out.println("Digite o arquivo:");

java.util.Scanner s = new Scanner(System.in);

String arquivo = s.next();

byte[] file = management.getFile(arquivo);

InputStream is = new ByteArrayInputStream(file);

System.out.println(file.length);

if (is != null) {
Writer writer = new StringWriter();

char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(is,
"UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
System.out.println(writer.toString());
}

System.out.println("Deseja excluir: (s/n)");

if(s.next().equals("s"))
System.out.println(management.deleteFile(arquivo));

System.out.println("FIM EXEC");
}
}

Veja que carregamos o contexto do Spring mostrando o endereço do arquivo e deste contexto apenas pegamos o bean que ele mesmo criou.

A partir daí é só rodar nosso métodos e é o nosso bom e velho código Java.

Simples assim.

Qualquer dúvida é só escrever.

Java RMI fácil utilizando o Spring

julho 15, 2011

Qualquer um que já precisou rodar alguma de suas classes Java remotamente já precisou fazer um servidor RMI.

Para quem não conhece vai um resumão BEM básico.

O RMI no Java, do Inglês, Remote Method Invocation é exatamente o que o seu nome diz, uma chamada de métodos de uma classe de forma remota.

Imagine que por algum motivo você está em uma máquina e precisa executar operações em outra máquina, apagar um arquivo, recuperar outro arquivo, tanto faz, a questão é que você precisa executar um comando na outra máquina. O que você faz?

Ou cria um container web todo ferrado na máquina e apela para um web-service ou faz um bom e velho RMI.

Até um tempo atrás fazer um servidor RMI era um trampo danado para falar a verdade, e até assustava um pouco os desenvolvedores para esta simples solução. A questão é que o Spring veio com tudo e resolveu toda esta complexidade.

Vamos lá.

Lá no meu trabalho me deparei com a seguinte situação:

Temos uma aplicação web com mensagens (parecido com uma mensagem de e-mail) salvas em um banco de dados. Estas mensagens vieram de um web-service, que salvou as mesmas no banco com o endereço dos anexos de um outro servidor, o repositório do cliente.

A questão é que este servidor não tem acesso ao mundo lá fora, portanto não dá pra fazer acesso direto de jeito nenhum.

O que precisamos fazer?

Montamos um servidor RMI nesta máquina repositório, este servidor RMI tem dois papéis, retornar um Array de Byte com o arquivo da máquina e excluir um arquivo passado.

Então nossa aplicação web simplesmente consome este servidor RMI, e passa um Path para pegar os arquivos, excluí-los e ser feliz para sempre.

Vamos lá para a criação do SERVIDOR RMI:

A primeira coisa que fiz foi um novo projeto Maven simples do eclipse.

Meu pom ficou com as seguintes dependências:


<dependencies>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rmi</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>

Não podemos esquecer que iremos empacotar esta aplicação em um jar executável com as dependências já resolvidas, portanto, os seguintes plugins serão necessários no Maven:


<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.sys.app.rmiserver.Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Feito isto podemos partir para o código de verdade. :)

A primeira coisa é a nossa classe Main, executável, meio que um EntryPoint do programa, como qualquer iniciante Java conhece nos seus sistemas de console.


import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* A classe <code>Main</code> inicia o serviço RMI ao carregar o Spring Application Context
*
* @author Leonardo Machado Moreira
*/

public class Main
{
public static void main( String[] args )
{
new ClassPathXmlApplicationContext("com/app/sys/rmiserver/rmi-server-context.xml");
}
}

Como podemos ver esta classe Main é ridícula. Na iremos apenas iniciar um contexto simples do Spring utilizando como configuração padrão o arquivo passado.

Esta localização é dentro da pasta resouces como qualquer recurso do Java, então, dentro de /src/main/resources/com/app/sys/rmiserver/rmi-server-context.xml

Este arquivo contém o seguinte conteúdo:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="rmiService" class="com.app.sys.rmiserver.DefaultFileManagement"/>
<bean>
<property name="serviceName" value="rmi-service"/>
<property name="service" ref="rmiService"/>
<property name="serviceInterface" value="com.app.sys.rmiserver.FileManagement"/>
<property name="registryPort" value="1099"/>
</bean>
</beans>

A primeira coisa que este arquivo faz é criar um bean chamado rmiService baseado na nossa classe DefaultFileManagement, que abordaremos daqui a pouco.

Na linha de baixo criamos outro bean da classe RmiServiceExporter responsável por abrir a porta de comunicação com o mundo e nomear um label do serviço.

Nossa codificação agora é bem simples.

Primeiro criamos uma Interface com todos os métodos que serão consumidos pelos clientes. Esta interface é a porta com o mundo, portanto deve ser exatamente o que você
implementou. No meu caso a interface FileManagement ficou assim:


/**
* A interface <code>FileManagement</code> é utilizada pelo Spring RMI para acesso remoto
*
* @author Leonardo Machado Moreira
*/

public interface FileManagement {

byte[] getFile(String path);
boolean deleteFile(String path);

}

Tão simples quanto poderia ser.

Agora é que vem onde a magia acontece. A implementação desta classe é exatamente o que nosso servidor fará e exatamente o que o Spring utilizou para criar o bean.

A classe DefaultFileManagement está desta forma:


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
* A classe <code>DefaultFileManagement</code> implementa FileManagement e
* realiza as ações de arquivo no servidor
*
* @author Leonardo Machado Moreira
*
* Retornado em Byte array para que a referencia exata seja passada, caso contrário o java não encontra o arquivo
*/
public class DefaultFileManagement implements FileManagement {

public byte[] getFile(String path) {
try {

File file = new File(path);
InputStream is = new FileInputStream(file);
long length = file.length();

if (length > Integer.MAX_VALUE)
throw new IllegalArgumentException("Arquivo muito grande");

byte[] bytes = new byte[(int) length];

int offset = 0;
int numRead = 0;

while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}

if (offset < bytes.length)
throw new IllegalArgumentException("Erro ao ler o arquivo");

is.close();

return bytes;

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

return null;
}

public boolean deleteFile(String path) {
return new File(path).delete();
}

}

De RMI mesmo esta classe não tem absolutamente nada, veja o método delefeFile por exemplo, tão ridículo quanto nosso Java IO permite ser.

Agora é a parte mais simples.

Utilizando o Maven faça um build.

Em pacote rodando um mvn package na pasta e pegue o jar com todas as dependências.

Cole-o na pasta do servidor e rode-o utilizando o comando

java -jar rmi-service.jar

Pronto, seu servidor RMI está criado.

No próximo posto mostro como consumir.

Maven 2 e a criação de projetos

junho 19, 2011

Não me lembro se já falei do Maven 2 para desenvolvimento Java, mas caso já tenha falado me repito: É uma ferramenta incrível para gerenciamento de estrutura de projetos e de dependências.

Quantas vezes você já não se pegou fazendo um control+c e control+v em um projeto, deletando as classes na mão e aí começando outro depois de renomear tudo que tem que renomear? Então, com o maven e um comando você poderia simplesmente através de uma estrutura pré-definida criar um projeto inteiro.

Onde trabalho temos um cliente relativamente chato que define os frameworks que devemos utilizar, a estrutura que o nosso projeto deve ter, em qual lugar a camada de serviço deve se encontrar e tudo mais… Então ao invez de ele mandar uma pasta inteira com o projeto e tudo mais ele simplesmente manda um Archetype do Maven 2 e através de um comando agente gera o projeto de onde quer que esteja.

Bom, instalar o Maven é simplão você só baixa, descompacta e adiciona ele a seu Path.

Criação de um projeto simples a partir de um archetype-quickstart é efetuada utilizando um comando como o seguinte:


mvn archetype:generate -DgroupId=br.eximia -DartifactId=simple -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Você percebe que os parâmetros são bastante auto-explicativos. Grupo, artefato e o nome do archetype do Maven.

Ao rodar o comando a seguir você só assiste o Maven criando uma pasta com toda a estrutura do seu projeto para você, até com um Hello World inclusive.

Lembrando que o Maven 2 não escolhe IDE e nem nada então uma coisa interessante após criar o seu projeto é destinar o mesmo ao Eclipse. Isto é feito através do comando:


mvn eclipse:eclipse

Aí você simplesmente importa este arquivo no Eclipse como um projeto Maven já existente. Para te ajudar neste projeto é bastante interessante que você instale o plugin m2Eclipse adicionando este repositorio aos seus updates e fazendo um next, next, next básico.

Note pela estrutura do projeto criado pelo Maven que este é um projeto de Console, então para a criação de um projeto web basta alterar o archetype, veja o comando:


mvn archetype:generate -DgroupId=br.eximia -DartifactId=simpleWeb -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

Note que o comando é exatamente o mesmo, eu só alterei o achetype, e aí temos um diretório chamado webapp e tudo mais que um projeto web precisa.

Como não é só porque um projeto é Web que ele tem código Java escrito o archetype não gera para você a pasta src/main/java, então você abre a sua estrutura e cria manualmente dentro de main a pasta java. Logo dentro de main haverão as pastas java, resources e webapp. De quebra eu já crio dentro de /src o diretório test e dentro deste o diretório java para abrigar os testes unitários. Então esta parte ficou com /src/test/java.

Agora um detalhe MUITO importante, da mesma forma que o comando para criar o projeto Console e o projeto web é diferente, o comando para fazer sua aplicação web suportar o Eclipse também é um pouco diferente.

Veja:


mvn eclipse:eclipse -Dwtpversion=2.0

Na verdade o comando é exatamente o mesmo, mas adicionamos um parametro que configura a versão do WTP, que é um plugin do Maven e do Eclipse para a configuração de aplicações web.

Após rodar este comando e importar o projeto no Eclipse verá que entenderá a estrutura do projeto com mais assertividade, verá também que ele gera os arquivos .classpath e .project do Eclipse.

Simples assim, agora é só colocar todas as dependencias do Maven dentro do seu POM que o Eclipse automaticamente baixará para o seu repositório local.

O legal que a partir de agora você já pode criar um servidor no seu eclipse, adicionar o projeto a ele e simplesmente iniciar o projeto, no meu caso o link http://localhost:8080/simpleWeb/, já estava funcionando com um Hello World.

Agora vem o motivador deste post. O site http://www.springfuse.com/, criar através de um archetype maven um projeto inteiro baseado em Spring, Spring WF, Primefaces e JPA, com um ótima estrutura que vale pena dar uma estudada.

É fantástico.

Absolutamente tudo (mínimo) sobre Charsets

maio 10, 2011

Charsets são um assunto um pouco místico para diversos desenvolvedores. Encontrei um artigo na internet de 2003, mas é relevante para muita gente. Com os devidos créditos, absolutamente tudo sobre charsets.

Ever wonder about that mysterious Content-Type tag? You know, the one you’re supposed to put in HTML and you never quite know what it should be?

Did you ever get an email from your friends in Bulgaria with the subject line “???? ?????? ??? ????”?

I’ve been dismayed to discover just how many software developers aren’t really completely up to speed on the mysterious world of character sets, encodings, Unicode, all that stuff. A couple of years ago, a beta tester for FogBUGZ was wondering whether it could handle incoming email in Japanese. Japanese? They have email in Japanese? I had no idea. When I looked closely at the commercial ActiveX control we were using to parse MIME email messages, we discovered it was doing exactly the wrong thing with character sets, so we actually had to write heroic code to undo the wrong conversion it had done and redo it correctly. When I looked into another commercial library, it, too, had a completely broken character code implementation. I corresponded with the developer of that package and he sort of thought they “couldn’t do anything about it.” Like many programmers, he just wished it would all blow over somehow.

But it won’t. When I discovered that the popular web development tool PHP has almost complete ignorance of character encoding issues, blithely using 8 bits for characters, making it darn near impossible to develop good international web applications, I thought, enough is enough.

So I have an announcement to make: if you are a programmer working in 2003 and you don’t know the basics of characters, character sets, encodings, and Unicode, and I catch you, I’m going to punish you by making you peel onions for 6 months in a submarine. I swear I will.

And one more thing:

IT’S NOT THAT HARD.

In this article I’ll fill you in on exactly what every working programmer should know. All that stuff about “plain text = ascii = characters are 8 bits” is not only wrong, it’s hopelessly wrong, and if you’re still programming that way, you’re not much better than a medical doctor who doesn’t believe in germs. Please do not write another line of code until you finish reading this article.

Before I get started, I should warn you that if you are one of those rare people who knows about internationalization, you are going to find my entire discussion a little bit oversimplified. I’m really just trying to set a minimum bar here so that everyone can understand what’s going on and can write code that has a hope of working with text in any language other than the subset of English that doesn’t include words with accents. And I should warn you that character handling is only a tiny portion of what it takes to create software that works internationally, but I can only write about one thing at a time so today it’s character sets.

A Historical Perspective

The easiest way to understand this stuff is to go chronologically.

You probably think I’m going to talk about very old character sets like EBCDIC here. Well, I won’t. EBCDIC is not relevant to your life. We don’t have to go that far back in time.

ASCII tableBack in the semi-olden days, when Unix was being invented and K&R were writing The C Programming Language, everything was very simple. EBCDIC was on its way out. The only characters that mattered were good old unaccented English letters, and we had a code for them called ASCII which was able to represent every character using a number between 32 and 127. Space was 32, the letter “A” was 65, etc. This could conveniently be stored in 7 bits. Most computers in those days were using 8-bit bytes, so not only could you store every possible ASCII character, but you had a whole bit to spare, which, if you were wicked, you could use for your own devious purposes: the dim bulbs at WordStar actually turned on the high bit to indicate the last letter in a word, condemning WordStar to English text only. Codes below 32 were called unprintable and were used for cussing. Just kidding. They were used for control characters, like 7 which made your computer beep and 12 which caused the current page of paper to go flying out of the printer and a new one to be fed in.

And all was good, assuming you were an English speaker.

Because bytes have room for up to eight bits, lots of people got to thinking, “gosh, we can use the codes 128-255 for our own purposes.” The trouble was, lots of people had this idea at the same time, and they had their own ideas of what should go where in the space from 128 to 255. The IBM-PC had something that came to be known as the OEM character set which provided some accented characters for European languages and a bunch of line drawing characters… horizontal bars, vertical bars, horizontal bars with little dingle-dangles dangling off the right side, etc., and you could use these line drawing characters to make spiffy boxes and lines on the screen, which you can still see running on the 8088 computer at your dry cleaners’. In fact  as soon as people started buying PCs outside of America all kinds of different OEM character sets were dreamed up, which all used the top 128 characters for their own purposes. For example on some PCs the character code 130 would display as é, but on computers sold in Israel it was the Hebrew letter Gimel (ג), so when Americans would send their résumés to Israel they would arrive as rגsumגs. In many cases, such as Russian, there were lots of different ideas of what to do with the upper-128 characters, so you couldn’t even reliably interchange Russian documents.

Eventually this OEM free-for-all got codified in the ANSI standard. In the ANSI standard, everybody agreed on what to do below 128, which was pretty much the same as ASCII, but there were lots of different ways to handle the characters from 128 and on up, depending on where you lived. These different systems were called code pages. So for example in Israel DOS used a code page called 862, while Greek users used 737. They were the same below 128 but different from 128 up, where all the funny letters resided. The national versions of MS-DOS had dozens of these code pages, handling everything from English to Icelandic and they even had a few “multilingual” code pages that could do Esperanto and Galician on the same computer! Wow! But getting, say, Hebrew and Greek on the same computer was a complete impossibility unless you wrote your own custom program that displayed everything using bitmapped graphics, because Hebrew and Greek required different code pages with different interpretations of the high numbers.

Meanwhile, in Asia, even more crazy things were going on to take into account the fact that Asian alphabets have thousands of letters, which were never going to fit into 8 bits. This was usually solved by the messy system called DBCS, the “double byte character set” in which some letters were stored in one byte and others took two. It was easy to move forward in a string, but dang near impossible to move backwards. Programmers were encouraged not to use s++ and s– to move backwards and forwards, but instead to call functions such as Windows’ AnsiNext and AnsiPrev which knew how to deal with the whole mess.

But still, most people just pretended that a byte was a character and a character was 8 bits and as long as you never moved a string from one computer to another, or spoke more than one language, it would sort of always work. But of course, as soon as the Internet happened, it became quite commonplace to move strings from one computer to another, and the whole mess came tumbling down. Luckily, Unicode had been invented.

Unicode

Unicode was a brave effort to create a single character set that included every reasonable writing system on the planet and some make-believe ones like Klingon, too. Some people are under the misconception that Unicode is simply a 16-bit code where each character takes 16 bits and therefore there are 65,536 possible characters. This is not, actually, correct. It is the single most common myth about Unicode, so if you thought that, don’t feel bad.

In fact, Unicode has a different way of thinking about characters, and you have to understand the Unicode way of thinking of things or nothing will make sense.

Until now, we’ve assumed that a letter maps to some bits which you can store on disk or in memory:

A -> 0100 0001

In Unicode, a letter maps to something called a code point which is still just a theoretical concept. How that code point is represented in memory or on disk is a whole nuther story.

In Unicode, the letter A is a platonic ideal. It’s just floating in heaven:

A

This platonic A is different than B, and different from a, but the same as A and A and A. The idea that A in a Times New Roman font is the same character as the A in a Helvetica font, but different from “a” in lower case, does not seem very controversial, but in some languages just figuring out what a letter is can cause controversy. Is the German letter ß a real letter or just a fancy way of writing ss? If a letter’s shape changes at the end of the word, is that a different letter? Hebrew says yes, Arabic says no. Anyway, the smart people at the Unicode consortium have been figuring this out for the last decade or so, accompanied by a great deal of highly political debate, and you don’t have to worry about it. They’ve figured it all out already.

Every platonic letter in every alphabet is assigned a magic number by the Unicode consortium which is written like this: U+0639.  This magic number is called a code point. The U+ means “Unicode” and the numbers are hexadecimal. U+0639 is the Arabic letter Ain. The English letter A would be U+0041. You can find them all using the charmap utility on Windows 2000/XP or visiting the Unicode web site.

There is no real limit on the number of letters that Unicode can define and in fact they have gone beyond 65,536 so not every unicode letter can really be squeezed into two bytes, but that was a myth anyway.

OK, so say we have a string:

Hello

which, in Unicode, corresponds to these five code points:

U+0048 U+0065 U+006C U+006C U+006F.

Just a bunch of code points. Numbers, really. We haven’t yet said anything about how to store this in memory or represent it in an email message.

Encodings

That’s where encodings come in.

The earliest idea for Unicode encoding, which led to the myth about the two bytes, was, hey, let’s just store those numbers in two bytes each. So Hello becomes

00 48 00 65 00 6C 00 6C 00 6F

Right? Not so fast! Couldn’t it also be:

48 00 65 00 6C 00 6C 00 6F 00 ?

Well, technically, yes, I do believe it could, and, in fact, early implementors wanted to be able to store their Unicode code points in high-endian or low-endian mode, whichever their particular CPU was fastest at, and lo, it was evening and it was morning and there were already two ways to store Unicode. So the people were forced to come up with the bizarre convention of storing a FE FF at the beginning of every Unicode string; this is called a Unicode Byte Order Mark and if you are swapping your high and low bytes it will look like a FF FE and the person reading your string will know that they have to swap every other byte. Phew. Not every Unicode string in the wild has a byte order mark at the beginning.

For a while it seemed like that might be good enough, but programmers were complaining. “Look at all those zeros!” they said, since they were Americans and they were looking at English text which rarely used code points above U+00FF. Also they were liberal hippies in California who wanted to conserve (sneer). If they were Texans they wouldn’t have minded guzzling twice the number of bytes. But those Californian wimps couldn’t bear the idea of doubling the amount of storage it took for strings, and anyway, there were already all these doggone documents out there using various ANSI and DBCS character sets and who’s going to convert them all? Moi? For this reason alone most people decided to ignore Unicode for several years and in the meantime things got worse.

Thus was invented the brilliant concept of UTF-8. UTF-8 was another system for storing your string of Unicode code points, those magic U+ numbers, in memory using 8 bit bytes. In UTF-8, every code point from 0-127 is stored in a single byte. Only code points 128 and above are stored using 2, 3, in fact, up to 6 bytes.

How UTF-8 works

This has the neat side effect that English text looks exactly the same in UTF-8 as it did in ASCII, so Americans don’t even notice anything wrong. Only the rest of the world has to jump through hoops. Specifically, Hello, which was U+0048 U+0065 U+006C U+006C U+006F, will be stored as 48 65 6C 6C 6F, which, behold! is the same as it was stored in ASCII, and ANSI, and every OEM character set on the planet. Now, if you are so bold as to use accented letters or Greek letters or Klingon letters, you’ll have to use several bytes to store a single code point, but the Americans will never notice. (UTF-8 also has the nice property that ignorant old string-processing code that wants to use a single 0 byte as the null-terminator will not truncate strings).

So far I’ve told you three ways of encoding Unicode. The traditional store-it-in-two-byte methods are called UCS-2 (because it has two bytes) or UTF-16 (because it has 16 bits), and you still have to figure out if it’s high-endian UCS-2 or low-endian UCS-2. And there’s the popular new UTF-8 standard which has the nice property of also working respectably if you have the happy coincidence of English text and braindead programs that are completely unaware that there is anything other than ASCII.

There are actually a bunch of other ways of encoding Unicode. There’s something called UTF-7, which is a lot like UTF-8 but guarantees that the high bit will always be zero, so that if you have to pass Unicode through some kind of draconian police-state email system that thinks 7 bits are quite enough, thank you it can still squeeze through unscathed. There’s UCS-4, which stores each code point in 4 bytes, which has the nice property that every single code point can be stored in the same number of bytes, but, golly, even the Texans wouldn’t be so bold as to waste that much memory.

And in fact now that you’re thinking of things in terms of platonic ideal letters which are represented by Unicode code points, those unicode code points can be encoded in any old-school encoding scheme, too! For example, you could encode the Unicode string for Hello (U+0048 U+0065 U+006C U+006C U+006F) in ASCII, or the old OEM Greek Encoding, or the Hebrew ANSI Encoding, or any of several hundred encodings that have been invented so far, with one catch: some of the letters might not show up! If there’s no equivalent for the Unicode code point you’re trying to represent in the encoding you’re trying to represent it in, you usually get a little question mark: ? or, if you’re really good, a box. Which did you get? -> �

There are hundreds of traditional encodings which can only store some code points correctly and change all the other code points into question marks. Some popular encodings of English text are Windows-1252 (the Windows 9x standard for Western European languages) and ISO-8859-1, aka Latin-1 (also useful for any Western European language). But try to store Russian or Hebrew letters in these encodings and you get a bunch of question marks. UTF 7, 8, 16, and 32 all have the nice property of being able to store any code point correctly.

The Single Most Important Fact About Encodings

If you completely forget everything I just explained, please remember one extremely important fact. It does not make sense to have a string without knowing what encoding it uses. You can no longer stick your head in the sand and pretend that “plain” text is ASCII.

There Ain’t No Such Thing As Plain Text.

If you have a string, in memory, in a file, or in an email message, you have to know what encoding it is in or you cannot interpret it or display it to users correctly.

Almost every stupid “my website looks like gibberish” or “she can’t read my emails when I use accents” problem comes down to one naive programmer who didn’t understand the simple fact that if you don’t tell me whether a particular string is encoded using UTF-8 or ASCII or ISO 8859-1 (Latin 1) or Windows 1252 (Western European), you simply cannot display it correctly or even figure out where it ends. There are over a hundred encodings and above code point 127, all bets are off.

How do we preserve this information about what encoding a string uses? Well, there are standard ways to do this. For an email message, you are expected to have a string in the header of the form

Content-Type: text/plain; charset=”UTF-8″

For a web page, the original idea was that the web server would return a similar Content-Type http header along with the web page itself — not in the HTML itself, but as one of the response headers that are sent before the HTML page.

This causes problems. Suppose you have a big web server with lots of sites and hundreds of pages contributed by lots of people in lots of different languages and all using whatever encoding their copy of Microsoft FrontPage saw fit to generate. The web server itself wouldn’t really know what encoding each file was written in, so it couldn’t send the Content-Type header.

It would be convenient if you could put the Content-Type of the HTML file right in the HTML file itself, using some kind of special tag. Of course this drove purists crazy… how can you read the HTML file until you know what encoding it’s in?! Luckily, almost every encoding in common use does the same thing with characters between 32 and 127, so you can always get this far on the HTML page without starting to use funny letters:

<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>

But that meta tag really has to be the very first thing in the <head> section because as soon as the web browser sees this tag it’s going to stop parsing the page and start over after reinterpreting the whole page using the encoding you specified.

What do web browsers do if they don’t find any Content-Type, either in the http headers or the meta tag? Internet Explorer actually does something quite interesting: it tries to guess, based on the frequency in which various bytes appear in typical text in typical encodings of various languages, what language and encoding was used. Because the various old 8 bit code pages tended to put their national letters in different ranges between 128 and 255, and because every human language has a different characteristic histogram of letter usage, this actually has a chance of working. It’s truly weird, but it does seem to work often enough that naïve web-page writers who never knew they needed a Content-Type header look at their page in a web browser and it looks ok, until one day, they write something that doesn’t exactly conform to the letter-frequency-distribution of their native language, and Internet Explorer decides it’s Korean and displays it thusly, proving, I think, the point that Postel’s Law about being “conservative in what you emit and liberal in what you accept” is quite frankly not a good engineering principle. Anyway, what does the poor reader of this website, which was written in Bulgarian but appears to be Korean (and not even cohesive Korean), do? He uses the View | Encoding menu and tries a bunch of different encodings (there are at least a dozen for Eastern European languages) until the picture comes in clearer. If he knew to do that, which most people don’t.

For the latest version of CityDesk, the web site management software published by my company, we decided to do everything internally in UCS-2 (two byte) Unicode, which is what Visual Basic, COM, and Windows NT/2000/XP use as their native string type. In C++ code we just declare strings as wchar_t (“wide char”) instead of char and use the wcs functions instead of the str functions (for example wcscat and wcslen instead of strcat and strlen). To create a literal UCS-2 string in C code you just put an L before it as so: L”Hello”.

When CityDesk publishes the web page, it converts it to UTF-8 encoding, which has been well supported by web browsers for many years. That’s the way all 29 language versions of Joel on Software are encoded and I have not yet heard a single person who has had any trouble viewing them.

This article is getting rather long, and I can’t possibly cover everything there is to know about character encodings and Unicode, but I hope that if you’ve read this far, you know enough to go back to programming, using antibiotics instead of leeches and spells, a task to which I will leave you now.

Ubuntu + Totem + MP3

maio 4, 2011

Assim que atualizei muito notebook para o Ubuntu 11.04 notei que somente no Totem, minhas músicas não avançavam e nem retrocediam na linha do tempo do tocador.

Busquei suas propriedades e tudo mais e nada.

Dei uma pesquisada e ví que o novo Ubuntu na sua atualização acaba apagando uns pacotes que ele diz ser inúteis, mas são bem úteis sim.

Vá no menu de aplicativos -> Central de Programas do Ubuntu

Lá em cima no canto superior direito na busca você digita apenas “Ubuntu”, estamos procurando por “Ubuntu restricted extras” que não passa de um monte de pacotes de compotentes protegidos por direitos autorais.

Assim que instalar você já consegue avançar e retroceder suas músicas.

Até mais.

Diquinhas bestas no Ubuntu – Numlock e Correção de Bateria

abril 27, 2011

Estas duas dicas são muito bestas e nem compensa falar muito sobre elas, mas vamos lá.

A primeira é o problema que 90% dos notebooks com Ubuntu instalado apresentem: quando você desconecta a bateria ele na hora fala que a energia está critica, que vai desligar e aí fica enxendo o saco também. E fica desligando sua tela.

Essa é bem simples, no terminal digite


gconftool-2 --type bool --set /apps/gnome-power-manager/general/use_time_for_policy false

Bem simples, agora é capaz que esteja corrigido o problema.

O segundo parece beta, mas é a melhor. Estava de saco cheio de ligar o NumLock toda vez que o sistema reiniciava.

Então um simples


sudo apt-get install numlockx

Funciona

Para preguiçoso mesmo, né.

Ta aí.

Remapeamento de Teclas no Ubuntu

abril 24, 2011

Há um tempo instalei o Ubuntu 10.10 no meu notebook Toshiba Satellite A665 e há um tempo convivo com um problema safado no Layout do teclado. É um notebook americano, logo, sem o cedilha (ç), manja?

Aí naquela seleção de padrão de teclado que o Ubuntu faz ele me deu o “EUA Internacional alternativo (antigo us_intl)”.

Beleza tudo bem com o cedilha, e tals, só que na tecla de acento, onde deveria ter a aspas duplas ele me dava uma aspinha pequena, meio feia, não sei exatamente o nome dela. Mas é bem simples distinguir, pois nos programas Java o compilador reclama.

Com este problema fui procurar um outro layout de teclado que resolvesse o meu problema, então encontrei o “EUA Internacional tecla morta (Alt GR)”. Este layout resolveu o problema da aspas duplas, só que ferrou com o cedilha que parou de funcionar. Fiquei um tempo convivendo com esta porcaria, só trocando de teclado quando eu ia desenvolver, até que enxeu o saco e daí saiu este post.

O Linux tem uma interessante função de mapeamento das teclas. Você pode fazer com que qualquer tecla do teclado tenha uma função definida por você, mesmo que não tenha nenhum sentido no layout padrão do teclado. Basta que utilize o comando xmodmap.

Vamos lá usar o meu problema como exemplo.

A primeira coisa a fazer é identificar o código da tecla que você está pressionando, para isto existe o programa xev. Abra o terminal e digite xev. Vai abrir um quadradinho preto e diversos códigos ficarão rolando na tela do terminal conforme você digita.

Veja:


KeyRelease event, serial 36, synthetic NO, window 0x4a00001,
root 0x15d, subw 0x0, time 1090414, (-410,293), root:(254,453),
state 0x10, <strong>keycode 36</strong> (keysym 0xff0d, <strong>Return</strong>), same_screen YES,
"   XLookupString gives 1 bytes: (0d) "
XFilterEvent returns: False

Atente para códigos com este padrão. Este código foi gerado ao pressionar a tecla enter. Em negrito os pontos importantes.

keycode 36: Representa o código da tecla pressionada

Return: Representa a função da tecla pressionada

Como eu disse no meu caso eu queria na mesma tecla (48, tecla da aspas e do acento agudo) adicionar funções de dois layouts diferentes do Ubuntu. Então eu mudeu para o layout de tecla morta (que possuí as aspas duplas) e usando o xev obtive os códigos abaixo descritos ao pressionar shift e aspas duplas.

Código: keycode 48

Função: quotedbl

Voltei o layout do teclado do Ubuntu para o que possuí o cedilha e obtive este código ao pressionar o botão do acento agudo (sem o shift)

Código: keycode 48

Função: dead_acute

Agora vem a parte simples, é só rodar o seguinte comando:

xmodmap -e “keycode 48 = dead_acute quotedbl”

Entre as aspas duplas é onde está a parte importante.

Você indica para a tecla número 48 as funções de dead_acute, que seria o meu acento agudo, e o quotedbl que seria as aspas duplas. Esta sequencia é importante, pois a primeira função indica o que eu quero que a tecla faça quando pressionada sem o shift, a segunda é ao pressionar com o shift.

Sacou?

Simples assim.

É só executar este comando que já está tudo legal, só tem um problema, ao reiniciar a máquina para de funcionar. Então dentro da sua pasta home você cria um arquivo chamado .xmodmaprc, com o seguinte conteúdo:

keycode 48 = dead_acute quotedbl

Simples assim.

Ao reiniciar o gnome, vai perguntar se você deseja carregar este arquivo, você diz que sim e já era, agora suas teclas estão mapeadas do jeito que você deseja.

Até mais

Dropbox e Linux – Can´t Sync Permission Denied

março 13, 2011

Rapidamente aos que não conhecem o Drobox lhes adianto que é uma fantástica ferramente FREE para sincronização de dados na Web entre as diversos computadores que você tem contato.

Apenas para lhes contextualizar tenho o Drobox no trabalho em um Windows 7 e em casa em um Ubuntu 10.10.

Importante lembrar que o Drobox de casa está em uma partição diferente da do sistema, partição esta que é NTFS. Isto é assim por que também tenho um Windows 7 em casa que nunca utilizo.

Recentemente formatei meu Ubuntu e ao instalar o Drobox recebi a fatídica mensagem de Can`t Sync, Permission Denied. Aí fiz o básico, um simples e irresponsável: chmod ugo=rwx -R /Dropbox

É eu liberei todos os direitos porque estava com pressa :)

E o pior é que não funcionou, aí dei uma pesquisada na internet e ví que no próprio forum da Dropbox o pessoal não entrava em um consenso. Teve gente mando desinstalar e instalar novamente, fazer um chown da pasta para o nome do seu usuário e outros disseram para colocar a pasta do Dropbox dentro da sua home.

Tirando a última gambiarra…ops solução, as outras não funcionavam mesmo, o pior é que o pessoal não lembrou do mais simples.

Uma partição NTFS no ambiente Linux dependendo da forma e dos direitos que é montada no fstab não permite chmod nem chown, aí não funciona mesmo e o pior dependendo dos direitos não permite que o daemon Dropbox coloque seus arquivos lá.

Então simples assim você vai em /etc/fstab e monta a sua partição NTFS com direitos de edição assim:

/dev/sda5     /media/Volume     ntfs-3g defaults,locale=pt_BR.utf8 0 0

Reinicia e vai estar lá um Dropbox novinho em folha.

Problemas com Encoding, Maven e Netbeans

fevereiro 23, 2011

Encoding é uma zica que atormenta todo e qualquer desenvolvedor. Se ele ainda tenta desenvolver em dois ambientes, Windows e Linux aí que o desespero aumenta.

Meu cenário é que no trabalho desenvolvo em Windows, codificação ISO-8859-1.

Em casa tenho o Ubuntu e o UTF-8.

Então começa a briga. Desenvolvo no trabalho, chego em casa e recebo o fatídico: unmappable character for encoding UTF8. Além de é claro todos os acentos estarem completamente destruídos. A primeira coisa para corrigir o projeto inteiro é alterar a codificação em que o mesmo está sendo desenvolvido, e isto fazemos no Maven, é claro.

No pom, é legal que sua área de properties fique mais ou menos desta forma


<properties>
<project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding>
<project.reporting.outputEncoding>ISO-8859-1</project.reporting.outputEncoding>
</properties>

A partir deste momento os arquivos de fonte do seu projeto no netbeans estarão corrigidos, mas é provável que  após uma limpeza e construção você veja que o problema de unmappable character for encoding UTF8 continua.

Isto é por que seu netbeans por default compila os arquivos em UTF-8, e você acabou de configurar seu projeto para ISO-8859-1.

Então vá onde seu netbeans está instalado, no arquivo /etc/netbeans.conf e adicione o código “-J-Dfile.encoding=UTF-8″ dentro da opção netbeans_default_options.

Então é só abrir e fechar o netbeans e está tudo ao normal.

O meu ficou assim:

netbeans_default_options=”-J-client -J-Xss2m -J-Xms32m -J-XX:PermSize=32m -J-XX:MaxPermSize=200m -J-Dapple.laf.useScreenMenuBar=true -J-Dapple.awt.graphics.UseQuartz=true -J-Dsun.java2d.noddraw=true -J-Dfile.encoding=UTF-8″


Seguir

Obtenha todo post novo entregue na sua caixa de entrada.