terça-feira, 29 de maio de 2007

Incrementando a performance do seu Debian/Ubuntu

Buenas tchê, depois de algum tempo brigando com a quantidade de
memoria que o novo Ubuntu Feisty devora (e já ter mudado pro Debian
Etch em definitivo em casa), posto aqui algumas dicas que ajudam quem
usa o seu SO ao extremo.

A primeira dica é para o boot concorrente e se aplica a quem tem
processadores multi-cores.
Editando o arquivo /etc/init.d/rc, procure pela string CONCURRENCY que
deve estar assim: CONCURRENCY=none. Altere ela pra CONCURRENCY=shell.
Pronto, mais fácil que tirar doce da boca de piá pançudo.

A segunda, esta sim violenta, principalmente pra quem passa o dia com
o Tomcat recebendo hot-deploys (pra quem nao sabe, o infame nao libera
a memoria que usa). O sistema, apos algum tempo de uso, insiste em
jogar coisas pro swap, mesmo ainda tendo alguns MB de memoria livres.
Pra acabar com a palhaçada, edite o arquivo /etc/sysctl.conf e
adicione a seguinte linha ao final:
vm.swappiness=0

Isso fará com que o sistema nao jogue dados pra swap, e evite ao
máximo usa-la. Mas, como nem tudo sao flores, se vc nao tem muita
memoria (tem apenas uma vaga lembrança) nao seja tão agressivo com o
sistema: Ele aceita valores entre 0 e 100. Dê alguma alegria pra ele!
;)


As dicas postadas aqui foram retiradas do artigo "Tweak ubuntu for
speed", que contem, alem dessas dicas, dicas que sao um pouco mais
arriscadas, que afetam o sistema de arquivos do seu sistema. Se vc for
corajoso (eu fiz, me ferrei e voltei) pode mandar bala.
Veja o artigo completo em:
http://tvease.net/wiki/index.php?title=Tweak_ubuntu_for_speed

terça-feira, 15 de maio de 2007

Montando unidades de rede Windows (samba) no Mac OS X

Por vários dias tive problemas para acessar a rede, pois o PC o qual eu queria conectar não aparecia em rede (nem o grupo do mesmo, sendo que ele era o único). Alterar o grupo não era a melhor opção, pois outras máquinas acessavam o mesmo perfeitamente!

Por burrice minha, não vi o menu do Finder: Ir e então Conectar ao servidor... igual ao do Ubuntu! As vezes me mato de vergonha...

Bom, a página http://lifehacker.com/software/mac-os-x/how-to-mount-a-windows-shared-folder-on-your-mac-247148.php, além de me resolver isto, ainda deu a dica para auto montar ao iniciar, a qual foi muito bem vinda!

É, as vezes tendemos a complicar o que está simples...

(P.S.: algum dia já falei que odeio alguns programas da Apple? O iTunes é o pior player que já vi, desde a época de windows até agora no Mac, e o Safari, enquanto fazia este post, simplesmente travou!)

segunda-feira, 14 de maio de 2007

Beryl + Netbeans

Ultimamente tenho perdido a paciencia com o beryl, pois, toda a vez
que eu abria o netbeans, uma tela cinza aparecia, me obrigando a
iniciar o gerenciador de janelas Metacity e depois o beryl novamente.
Como quase tudo na vida de um usuario linux sao flores (ficou meio
gay, mas tudo bem), há uma solução, e é simples como tirar uma chuleta
da boca de um pittbul com um dente cariado.
Simplesmente adicione a linha abaixo no final do arquivo /etc/environment:
AWT_TOOLKIT="MToolkit"


E voilà!

quarta-feira, 9 de maio de 2007

Reinstalação completa no Debian (Ubuntu)

Esta dica serve, principalmente, para quando precisarmos atualizar o sistema.
Fazendo uma atualização limpa, perde-se os softwares instalados (as
configurações presume-se que o usuario tenha numa partição
(/home/usuario) a parte. Mas há uma maneira de guardarmos a lista de
sofwares que temos instalados para posterior reinstalação
automatizada, seguindo os dois simples passos abaixo.

No seu sistema condenado, rode o seguinde comando:
dpkg --get-selections | grep -v deinstall > ProgramasInstalados.bkp

Apos a reinstalação do sistema, restaure seus programas com o comando:
dpkg --set-selections < ProgramasInstalados.bkp

E pronto! Sistema novo!

terça-feira, 8 de maio de 2007

Reformatar código não funcionando

Hoje estava trabalhando em um fonte e necessitei reformatá-lo, quando me deparei que esta função não estava fazendo nada, mesmo quando ia pelo menu (achei inicialmente que existia algum conflito com meus atalhos).

A dica veio do Diego, onde o plugin "JavaScript Editor", versão 0.2, causa esta incompatibilidade.

Apenas desabilitá-lo e o problema foi resolvido. Interessante foi não localizar problemas similares com outros usuários... fica aqui o registro.

Já tive este plugin funcionando outras vezes, provavelmente deve ser alguma compatibilidade com o idioma português... irei testar em outro momento.

Alteração: precisei mexer em um JS e então habilitei o plugin. Agora ambos funcionam... vai entender...

sexta-feira, 4 de maio de 2007

tirando beep do terminal linux (tty)

Para silenciar o seu terminal (ele faz um barulhinho chato quando usamos tab) edite o arquivo /etc/inputrc e adicione a seguinte linha:
set bell-style none
É possivel que ela esteja comentada, necessitando somente descomentá-la.
Reinicie o terminal e voilá!

quinta-feira, 3 de maio de 2007

Implementando um sistema de login com JAAS no Tomcat

O que é?
O JAAS (Java Authentication and Authorization Service) é uma API para login e controle de acesso de usuários.

Proposta
Implementar um sistema de autenticação personalizado, pois os oferecidos não se adequavam a proposta inicial do sistema.

Problemas Encontrados
Como fazer a comunicação entre o sistema, que está rodando na web com o módulo de login, que está rodando por baixo do Tomcat.

Implementação
O módulo de login foi organizando com a seguinte estrutura:
  • imagemfilmes.auth -> Pacote que contém meu módulo de login
  • imagemfilmes.auth.obj -> Pacote que contém meus objetos auxiliares
  • imagemfilmes.auth.principals -> Pacote que contém as classes extendidas de Principals
IFLoginModule.java
/**
* Modulo de login da ImagemFilmes.
* @author Diego Fincatto
* @since 1.0
*/
public class IFLoginModule implements LoginModule {

private boolean commitSucedido = false;
private boolean operacaoSucedida = false;

private User usuario;
private Connection connection;
private Banco banco;

protected Subject subject;
protected CallbackHandler callbackHandler;
protected Map sharedState;


/**
* Metodo que faz a leitura dos parametros passados.
* @param subject Subject.
* @param callbackHandler CallbackHandler.
* @param sharedState SharedState.
* @param options Mapa com as opcoes definidas no login.conf.
*/
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;

//cria um novo banco
Banco banco = new Banco();
banco.setSqlUser((String) options.get("sqlUser"));
banco.setSqlRoles((String) options.get("sqlRoles"));
banco.setBdDriver((String) options.get("bdDriver"));
banco.setBdUser((String) options.get("bdUser"));
banco.setBdPass((String) options.get("bdPass"));
banco.setBdURL((String) options.get("bdURL"));
this.setBanco(banco);
}

/**
* Metodo que efetua o login do usuario.
* @throws javax.security.auth.login.LoginException Caso nao pode efetuar o
* login.
* @return true.
*/
public boolean login() throws LoginException {
Connection conn = null;
try {
conn = this.getConnection();

// recupera o login e senha informados no form
Usuario usrCallBack = this.buscaUsuarioCallback(this.callbackHandler);

// valida o usuario
this.usuario = this.validaUsuario(conn, banco, usrCallBack);
} catch (ClassNotFoundException cnfe) {
this.operacaoSucedida = false;
throw new LoginException("Erro ao conectar com o banco: " + cnfe.getMessage());
} catch (SQLException e) {
this.operacaoSucedida = false;
throw new LoginException("Erro ao obter conexao: " + e.getClass().getName() + ": " + e.getMessage());
} finally {
try {conn.close();} catch (SQLException e) {}
}

// acidiona o usuario e roles no mapa de compartilhamento
this.sharedState.put("javax.security.auth.principal", this.usuario);
this.sharedState.put("javax.security.auth.roles", this.usuario.getRoles());

//remove mensagem de erro
this.setMensagem(this.usuario.getName(), "");

//retorna ok
return true;
}

/**
* Metodo executado depois das funcoes de login e logout.
* @throws javax.security.auth.login.LoginException Caso erro.
* @return true.
*/
public boolean commit() throws LoginException {
// adiciona o usuario no principals
if (this.usuario != null && !subject.getPrincipals().contains(this.usuario)) {
subject.getPrincipals().add(this.usuario);
}

// adiciona as roles no principals
if ( (this.usuario!=null) && (this.usuario.getRoles() != null) ){
for(Role role : this.usuario.getRoles()){
if (!subject.getPrincipals().contains(role)) {
subject.getPrincipals().add(role);
}
}
}

//seta o commit como feito
this.commitSucedido = true;

//retorna ok
return true;
}

/**
* Aborta alguma operacao.
* @throws javax.security.auth.login.LoginException Caso erro.
* @return true.
*/
public boolean abort() throws LoginException {
if (!this.operacaoSucedida) {
return false;
} else if (this.operacaoSucedida && !this.commitSucedido) {
this.operacaoSucedida = false;
} else {
this.operacaoSucedida = false;
this.logout();
}

//limpa os dados da autenticacao
this.subject = null;
this.callbackHandler = null;
this.sharedState = null;
this.usuario.setRoles(new HashSet());

//retorna o estado da operacao
return this.operacaoSucedida;
}

/**
* Metodo que executa logout no sistema.
* @throws javax.security.auth.login.LoginException Caso erro.
* @return true.
*/
public boolean logout() throws LoginException {
// remove o usuario e as roles do principals
subject.getPrincipals().clear();

//retorna ok
return true;
}



/**
* Este eh o metodo responsavel por validar o usuario no logon.
* Se conseguir validar, ja busca as Roles associadas a ele.
* @param conn Conexao com banco.
* @param _usuario Usuario a ser validado.
* @throws javax.security.auth.login.LoginException Caso nao consiga validar
* o usuário.
* @return true.
*/
private User validaUsuario(Connection conn, Banco _banco, Usuario _usuario) throws LoginException {
User retValue = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = conn.prepareStatement(_banco.getSqlUser());
pstmt.setString(1, _usuario.getLogin());

//executa a query
rs = pstmt.executeQuery();

//verifica se o usuario existe na tabela
if (rs.next()) {
String senhaBanco = rs.getString(1);

//verifica se as senhas batem
if (senhaBanco.equals(this.geraMD5(_usuario.getSenha()))){
retValue = new User(_usuario.getLogin());
retValue.setRoles(this.recuperaRoles(conn, _banco, _usuario));
} else {
this.setMensagem(_usuario.getLogin(), "Senha Invalida!");
throw new LoginException("Senha Invalida!");
}
} else {
this.operacaoSucedida = false;
this.setMensagem(_usuario.getLogin(), "Usuario nao localizado!");
throw new LoginException("Usuario nao localizado!");
}

//atualiza o numero de logins efetuados
StringBuilder sql = new StringBuilder();
sql.append("UPDATE usuario_site ");
sql.append("SET acessonum = acessonum+1 ");
sql.append("WHERE email = ? ");

pstmt = conn.prepareStatement(sql.toString());
pstmt.setString(1, _usuario.getLogin());
pstmt.executeUpdate();

//retorna o usuario
return retValue;
} catch (SQLException e) {
this.operacaoSucedida = false;
throw new LoginException("Erro de SQL");
} finally {
try {rs.close();} catch (Exception e) {}
try {pstmt.close();} catch (Exception e) {}
}
}

/**
* Metodo que recupera as roles do usuario.
*
* @return true.
* @param _banco Banco a ser utilizado.
* @param conn Conexao com o banco.
* @param _usuario Usuario a ser pesquisado.
* @throws javax.security.auth.login.LoginException Caso erro.
*/
public Set recuperaRoles(Connection conn, Banco _banco, Usuario _usuario) throws LoginException {
Set retValue = new HashSet();
PreparedStatement statement = null;
ResultSet rs = null;
try {
statement = conn.prepareStatement(_banco.getSqlRoles());
statement.setString(1, _usuario.getLogin());
rs = statement.executeQuery();

//percorre os resultados
while (rs.next()) {
retValue.add(new Role(rs.getString(1)));
}

//adiciona roles estaticas
} catch (SQLException e) {
this.operacaoSucedida = false;
this.setMensagem(_usuario.getLogin(), "Erro ao recuperar roles!");
throw new LoginException("Erro ao recuperar roles!");
} finally {
try {rs.close();} catch (Exception e) {}
try {statement.close();} catch (Exception e) {}
}

//retorna
return retValue;
}

/**
* Este eh o metodo que pega o login e senha informados, independente do
* metodo usado para a autenticacao.
*
* @param _callback Callback de onde virao as informacoes.
* @throws javax.security.auth.login.LoginException Caso erro.
* @return Usuario.
*/
protected Usuario buscaUsuarioCallback(CallbackHandler _callback) throws LoginException {
Usuario retValue = null;
if (_callback == null){
throw new LoginException("Nao foi encontrado um CallbackHandler!");
}

//cria um array de calback para pegar nome de usuario e senha
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("Login");
callbacks[1] = new PasswordCallback("Senha", false);

try {
//pega o callback
_callback.handle(callbacks);
//cria um usuario
retValue = new Usuario();
retValue.setLogin(((NameCallback) callbacks[0]).getName());
retValue.setSenha(new String(((PasswordCallback) callbacks[1]).getPassword()));

//limpa a senha
((PasswordCallback) callbacks[1]).clearPassword();
} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException uce) {
throw new LoginException(uce.getCallback().toString() + " nao disponivel!");
}

//retorna o usuario
return retValue;
}
/**
* Funcao que gera MD5
* @param _str String que queremos gerar a md5
* @return retorna o md5 do parametro passado
*/
public static String geraMD5(String _str){
String retValue = "";
try{
//cria uma instancia de Messagedigest
MessageDigest md5 = MessageDigest.getInstance("MD5");
//gera md5
md5.update(_str.getBytes());
byte[] hash = md5.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < retvalue =" hexString.toString();" connection ="="" connection =" DriverManager.getConnection(this.getBanco().getBdURL()," banco =" banco;">

Banco.java
**
* Classe que carrega as informacoes do banco de dados.
* @author Diego Fincatto
* @since 1.0
*/
public class Banco {

private String sqlUser;
private String sqlRoles;
private String bdDriver;
private String bdUser;
private String bdPass ;
private String bdURL;

/** Creates a new instance of Banco */
public Banco() {
this.setSqlUser("");
this.setSqlRoles("");
this.setBdDriver("");
this.setBdUser("");
this.setBdPass("");
this.setBdURL("");
}

public String getSqlUser() {
return sqlUser;
}

public void setSqlUser(String sqlUser) {
this.sqlUser = sqlUser;
}

public String getSqlRoles() {
return sqlRoles;
}

public void setSqlRoles(String sqlRoles) {
this.sqlRoles = sqlRoles;
}

public String getBdDriver() {
return bdDriver;
}

public void setBdDriver(String bdDriver) {
this.bdDriver = bdDriver;
}

public String getBdUser() {
return bdUser;
}

public void setBdUser(String bdUser) {
this.bdUser = bdUser;
}

public String getBdPass() {
return bdPass;
}

public void setBdPass(String bdPass) {
this.bdPass = bdPass;
}

public String getBdURL() {
return bdURL;
}

public void setBdURL(String bdURL) {
this.bdURL = bdURL;
}

}
Usuario.java
/**
* Classe que representa um usuario.
* @author Diego Fincatto
* @since 1.0
*/
public class Usuario {
private String login;
private String senha;

/** Creates a new instance of Usuario */
public Usuario() {
this.setLogin("");
this.setSenha("");
}

public String getLogin() {
return login;
}

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

public String getSenha() {
return senha;
}

public void setSenha(String senha) {
this.senha = senha;
}

}
Role.java
/**
* Classe que representa uma role.
* @author Diego Fincatto
* @since 1.0
*/
public class Role implements Principal{

private String name;

public Role(String name){
this.name = name;
}

public String getName() {
return name;
}
}
User.java
/**
* Classe que representa um usuario.
* @author Diego Fincatto
* @since 1.0
*/
public class User implements Principal {
private String name;
private Set roles;

public User(String name){
this.setName(name);
}

private void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public Set getRoles() {
return roles;
}

public void setRoles(Set roles) {
this.roles = roles;
}
}






Configurações Necessárias
Primeiramente, precisamos criar um arquivo onde dizemos para a API de autenticação do Java que dispomos de um sistema de login personalizado. O mesmo é feito criando o seguinte arquivo, chamado login.config:
IF {
imagemfilmes.auth.IFLoginModule required
dataSourceName="imagem"
sqlUser="select senha from usuario where login=? and ativo = true"
sqlRoles="select role from usuario_roles where login=?"
bdDriver="org.postgresql.Driver"
bdUser="usuario"
bdPass="senha"
bdURL="jdbc:postgresql://ipdoservidor/banco"
;
};
Explicando rapidamente as opções do arquivo, o IF é o "nome" do modulo. imagemfilmes.auth.IFLoginModule são, respectivamente, o nome do pacote e da classe que fará a autenticação. Daí pra baixo são opções que serão passadas para o modulo de login para podermos trabalhar la dentro, como veremos mais adiante. Nada disso é necessário, embora seja interessante usar, pois, caso algum desses parametros mude, é só mudar as configurações desses arquivo e tudo volta a funcionar. Caso o desenvolvedor optasse por fixar dentro do código, a cada mudança seria necessário uma nova compilação e um novo deploy do arquivo.
OBS: Percebam que implementei no PostgreSQL.

Após a criação do arquivo, devemos dizer ao java que ele existe (pois o java não é pai de santo pra adivinhar né?). Fazemos isso editando o arquivo $JAVA_HOME/jre/lib/security/java.security. Adicionamos o nosso modulo de login ao arquivo de configuração, como na linha que segue login.config.url.1=file:/lugarondevocecriouoarquivo/login.config.

E para que a aplicação possa usar o modulo, dizemos pra ela que o faça, modificando seu contexto como segue:


Basicamente, adicionamos um Realm ao contexto, informando o tipo (classname), o nome da aplicacao, definido lá no login.config, e as classes de User e Role que foram implementadas (Vide capitulo da implementacao).

Socorro
Alguma coisa não está saindo como o esperado? Não se desespere!!! Para tudo nessa vida há alguma solução(embora na maioria das vezes nós não saibamos qual é a merda da solução!!!).
Você poderá debugar o seu módulo iniciando seu tomcat com os seguintes parametros:
-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n

Finalizando
E é isso pessoal, muita coisa pode ser feita para que se ajuste as nossas necessidades. O JAAS é uma API excelente, embora poucos desenvolvedores a usam (pelo menos como deveriam).

Créditos
Abaixo cito as fontes onde busquei inspiração para a resolução dos problemas de percurso encontrados.
  1. http://java.sun.com/products/jaas/
  2. http://www.guj.com.br/posts/list/38988.java

NetBeans com SVN em português

Há alguns dias estou trabalhando com um iMac (em outro post conto detalhes desta migração...) e estou ainda me adaptando.

Um problema encontrado, após a configuração do SVN, para utilização no NetBeans (que foi resolvida com esta dica) foram as mensagens de commit, que não podiam ter acentuação!
Sempre que colocava uma mensagem com acentuação, retornava uma mensagem de erro de commit "Can't convert string from native encoding to 'UTF-8'"...

Com algumas buscas, encontrei uma ajuda no livro do subversion, no capítulo de internacionalização, confirmei que o locale estava como C.
Ao definir o mesmo para pt_BR.UTF-8, com o comando export LC_ALL=pt_BR.UTF-8, esta definição ficava apenas para o terminal aberto.

A solução? Incluí esta linha no lançador do NetBeans, nano -w /Applications/NetBeans.app/Contents/Resources/NetBeans/bin/netbeans, e, voilà! Acentuação funcionando!

Alteração no post: apesar de ter baixado NetBeans em português, o mesmo sempre ficou em inglês... isto não era um problema, mas era, no mínimo, curioso. Com esta linha, agora o mesmo também ficou em português!

Segunda alteração: a idéia inicial era não afetar o sistema, apenas no NetBeans. Porém várias vezes uso o SVN no terminal, então colocar esta linha diretamente no /etc/profile é mais adequado...