Dica rápida, configurando teclado Ubuntu layout Inglês Internacional.

Abra seu console e altere seguinte arquivo:


sudo gedit /usr/lib/gtk-2.0/2.10.0/immodule-files.d/libgtk2.0-0.immodules
<pre>

Procure a linha abaixo e adicione <strong>:en</strong> ao final da linha como segue:


"/usr/lib/gtk-2.0/2.10.0/immodules/im-cedilla.so"
"cedilla" "Cedilla" "gtk20" "/usr/share/locale" "az:ca:co:fr:gv:oc:pt:sq:tr:wa<strong>:</strong>en"

Salve  arquivo, neste ponto as aplicações GTK, agora vamos configurar para as aplicações QT<strong>.</strong>
Altere o arquivo "Compose" conforme abaixo alterando todas as ocorrências de <strong>ć</strong> e <strong>Ć</strong> para <strong>ç </strong>e<strong> Ç </strong>respectivamente.


sudo gedit /usr/share/X11/locale/en_US.UTF-8/Compose

Após alterado todas as ocorrências reinicie e pronto.

Abraços.

Ronaldo.

Salve galera.

Vou postar abaixo uma classe chamada SharedObjectManager, que faz o controle de login de um usuário em uma aplicação Flex, ela armazena os dados de login do usuário em um SharedObject na máquina do usuário, em caso de dar fechar a janela ou apertar F5 do navegador, o usuário ainda permanece logado.

OBS: Utilizo esta classe com dois módulos (Login e Main) onde modulo Main é a aplicação em si. Esta classe você pode implementar a logica de negócio que precisar.

A classe LoginModelo é um simples VO que armazena as informações necessárias do usuário, você deve customizar ela de acordo com a sua necessidade.

Segue classe:

package br.com.ronaldorigoni.model{
import mx.collections.ArrayCollection;

public class LoginModelo{

private var _id:uint;
public function set id (value:uint):void{
{
_id = value;
}

public function get id ():uint{
return _id;
}

private var _login:String;
public function set login (value:String):void{
{
_login = value;
}

public function get login ():String{
return _login;
}

private var _password:String;
public function set password (value:String):void{
{
_password = value;
}

public function get password ():String{
return _password;
}

private var _permissions:ArrayCollection;
public function set permissions (value:ArrayCollection):void{
{
_permissions = value;
}

public function get permissions ():ArrayCollection{
return _permissions;
}

}
}

E a classe SharedObjectManager:


package br.com.ronaldorigoni.sharedObject{
import br.com.ronaldorigoni.model.LoginModelo;
import flash.net.SharedObject;

/**
* Classe que faz o controle de login de um usuário na aplicação.
* Basea-se em carregamento de dois modulos, login e main.
* @autor Ronaldo Rigoni.
**/
public class SharedObjectManager{

/**
* Objeto SharedObjetc privado.
**/
private static const SHARED_OBJECT:SharedObject = SharedObject.getLocal("userSharedObject");

public function SharedObjectManager(){
}
/**
* Funcao init(). Verifica se o usuário ja está autenticado na aplicação
* E controla qual modulo será carregado.
* Se ja estiver logado será o modulo principal.
* Caso contrario será exibido a tela de login.
**/
public function init():void{
if(verificarStatusLogin()){
loadMainModule();
}
else{
loadLoginModule();
}
}

/**
* Limpa o sharedObject se o mesmo não estiver nulo.
**/
private function clearSharedObject(event:Event):void{
if(SHARED_OBJECT != null){
SHARED_OBJECT.clear();
SHARED_OBJECT.flush();
}
}

/**
* Verifica status de login na aplicação.
* Aqui você pode aplicar outras regras de negocio
* para validar se o usuário esta logado ou nao,
* como por ex: tempo do ultimo login se ainda está valido...
**/
private function verificarStatusLogin():Boolean{
if(SHARED_OBJECT != null){
if(SHARED_OBJECT.data.loginModelo)
return true;
}
return false;
}

/**
* Carrega o modulo de login.
**/
private function loadLoginModule():void{
// codigo para carregar seu modulo de login
}

/**
* Carrega o modulo principal da aplicação se o usuário
* estiver autenticado.
**/
private function loadMainModule(loginModelo:LoginModelo = null):void{
// se loginModelo for null significa que usuário já está logado.
if(loginModelo != null){
saveLoginModelo(loginModelo);
}
// aqui codigo para carregar seu modulo principal
}
/**
* Salva os dados de login do usuário.
* @param loginModelo LoginModelo
**/
private function saveLoginModelo(loginModelo:LoginModelo):void{
SHARED_OBJECT.data.loginModelo = loginModelo;
SHARED_OBJECT.flush();
}
/**
* Recupera as informacoes de login.
* @return <code>loginModelo</code> caso estiver autenticado
*            <code>null</code> caso não estiver autenticado
**/
public function retrieveLoginModelo():LoginModelo{
if(SHARED_OBJECT  != null){
var modelo:LoginModelo = SHARED_OBJECT.data.loginModelo as LoginModelo;
return modelo;
}else{
return null;
}
}

}
}

Abraço e até um próximo post.

Ronaldo

Sunday, August 2, 2009Rodando um jar pelo console no Linux

Salve galera,

Dica rápida,

Se quiser executar um jar pelo console de forma que  o console não fique "preso", isso ocorre com frequência quando precisamos inicializar um serviço java remotamente você precisa fechar os Streams de saida do Java na classe System conforme abaixo.


/**
* Fecha o fluxo de saida para o console.
*/
private static void closeOutStreams(){
System.out.close();
System.err.close();
}

Após isso basta inicializar seu jar conforme abaixo.


java -jar meuJar.jar &

Ronaldo.

Saturday, August 1, 2009Introdução a JQuery

Olá pessoal,

Vou abordar uma série de posts sobre JQuery, desde a introdução até efeitos avançados de layout, bem como ajax e a customização de plugins.

Oque é JQuery?

JQuery é uma biblioteca JavaScript criada por John Resig, de código aberto, liberada sob a licença GNU (General Public Licence). É uma biblioteca simples que possibilita não apenas programadores experientes bem como designers e quem possue pouco conhecimento de programação.

O  desenvolvimento com JQuery está centrado em simplicidade, onde você não precisará mais ter que escrever linhas e linhas de código JS para poder selecionar um elemento DOM de sua página, com apenas um método da JQuery este trabalho todo estará eliminado, e já possuirá o elemento DOM em mãos para poder manipulá-lo , validá-lo etc.

Qual a finalidade da JQuery?

  1. Efeitos visuais e animações.
  2. Acesso e manipulaçao de objetos DOM.
  3. Ajax.
  4. Interabilidade.
  5. Alteração de conteúdos.
  6. Modificar layout.
  7. Redução de código JS.

JQuery está em conformidade com todos os padrões WEB estipulados pela W3C, ela oferece total suporte a CSS3, é uma biblioteca compatível com qualquer navegador (cross browser). JQuery visa incrementar de forma progressiva e não obstrutiva a usabilidade e acessibilidade.

OBS: Isso não significa que todo código escrito com Jquery se torna um código válido segundo os padões WEB, isso cabe a cada desenvolvedor escrever seus códigos em conformidade com padrões e acessibilidade.

Principais características:

  1. Utiliza seletores CSS para busca de elementos DOM na árvore HTML.
  2. Arquitetura simples para instalação de plugins e criação de plugins.
  3. Totalmente cross browser.
  4. Não é necessário a criação de loops para busca de elementos na árvore DOM.
  5. Programação encadeada, pois todo método retorna um objeto.
  6. Extensível, permite a você extender a própria biblioteca e customizar a seu modo.

Como instalar?

JQuery não necessita instalação, para você fazer uso a biblioteca basta apenas acessar o site Jquery.com baixe a versão minified, que se trata de um arquivo comprimido; No momento da escrita deste post a versão atual estável é 1.3.1, apos baixar a biblioteca, para linkar o arquivo JS vc deverá fazer isso dentro da head desta forma:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" charset="UTF-8" src="js/jquery-1.3.1.min.js"></script>
<title>Hello word JQuery</title>
</head>
<body>
</body>
</html>

Após feito isso vamos criar outro arquivo JavaScript onde criaremos nosso primeiro HelloWord em Jquery. Crie na pasta js do seu projeto um arquivo chamado helloword.js, e adicione o seguinte código dentro dele:


/**
* O Construtor da jquery é baseado no caracter $
* Todo seletor deve passar por ele, neste caso estamos passando
* o próprio document e quando o document terminar de carregar oque
* estiver dentro da função passada como parametro será executado.
*/
$(document).ready(function(){
// Exibindo mensagem logo apost termino de carregamento
// da página
alert('A pagina terminou de carregar, Hello Word');
});

Para que isso funcione altere a head da  sua página html conforme abaixo:


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" charset="UTF-8" src="js/jquery-1.3.1.min.js"></script>
<script type="text/javascript" charset="UTF-8" src="js/helloword.js"></script>
<title>Hello word JQuery</title>
</head>
<body>
</body>
</html>

Desta forma quando acessada a página index.html será exibida uma mensagem de HelloWord, faça o teste aqui, o download deste projeto de exemplo está aqui.

No próximo post vamos abordar a sintaxe JQuery, e inicio de uso de seletores básicos.

Abraços e até um próximo post.

Ronaldo.

Buenas Galera, vamos abordar aqui um framework simples para se trabalhar com Xml Sockets entre Java e Flex.

O Framework abordado se chama Apache Mina Framework que implementa a nova API Java NIO , onde foi otimizado muito o processamento de IO em Java.

O que é Apache Mina?

Apache Mina é indicado para quem precisa desenvolver um servidor utilizando um protocolo expecífico, ou em um protocolo novo
onde você mesmo poderá implementa-lo, e que necessite de escalabilidade e boa performace. Onde você possue tempo apertado e o cronograma não permite desenvover um servidor do zero.

Apache MINA ( A Multi-purpose Infrastructure for Network Applications)

MINA e um framework para aplicações em rede, desenvolvido em java, com um conjunto de API para capturar eventos assincronamente, auxilia facilmente a desenvolver aplicações que requer conectividade, com uma alta performace e alta escalabilidade.
Pode ser desenvolvido com o MINA um servidor com um protocolo especifico sem ficar escovando bits, e com uma performace e desempenho considerável, permitindo separar a logica de conectividade da logica do protocolo. Diversas camadas de transporte(*Acceptor) já esta implementado no framework como TCP/IP , UDP/ID e Porta Serial, mas você pode desenvolver a sua e plugar.
Podemos resumir o MINA como um conjunto de classes `templates`, bastando o desenvolvedor se preocupar, com a logica do seu protocolo.

Documentação

Start Guide

Oque é Xml Sockets?

Há dois tipos diferentes de conexões de Socket possíveis no ActionScript 3.0: Conexões por XML Sockets e conexões por Binary Socket. Um XML Socket permite que você se conecte com um servidor remoto e crie uma conexão de servidor que permaneça aberta até que seja fechada explicitamente. Isso permite a troca de dados XML entre um servidor e um cliente sem necessidade de abrir continuamente novas conexões de servidor. Outro benefício do uso de um XML Socket é que o usuário não precisa solicitar dados explicitamente. É possível enviar dados do servidor sem solicitações e enviar dados a cada cliente conectado com o XML Socket.

As conexões de XML Socket requerem a presença de uma política de Socket no servidor de destino.

Uma conexão de Binary Socket é semelhante a um XML Socket, exceto que o cliente e o servidor não precisam trocar pacotes XML especificamente. Em vez disso, a conexão pode transferir dados como informações binárias. Isso permite conectar-se a uma ampla faixa de serviços, incluindo servidores de email (POP3, SMTP e IMAP) e novos servidores (NNTP).

Bom vamos ao que intereça.

Baixe os fontes do Mina aqui

Crie um projeto Java e adicione os Jars do Mina no seu classpath.

O Mina possue um procolo já implementado chamado TextLineCodecFactory que se baseia em procolo de texto puro,

onde  o final de cada requisição é um \n ou seja um ENTER.

Mas este procolo não nos ajuda muito, então iremos implementar nosso próprio protocolo chamado XmlProtocolCodecFactory e em seguida vamos implementar nosso servidor e o Handler que receberá e tratará as requisições.

Aqui a criação do XmlDecoder, responsável por decodificar uma requisição e transformar a mesma em um Document.


package br.com.ronaldorigoni.mina.codec;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
* Decoder de XML, responsável por interceptar a requisiçao do usuário
* e transformar a mesma em um Objeto DOM, e passar o mesmo para o Handler
* manipular.
* @author Ronaldo Rigoni
*/
public class XmlDecoder extends ProtocolDecoderAdapter {
/**
* Decodifica a requisiçao e passa a mesma para o handler.
*/
public void decode(IoSession session, IoBuffer buffer, ProtocolDecoderOutput output) throws Exception {
output.write(parserXML(buffer));
}

/**
* Efetua o parser dos bits recebidos e transforma em um Document
* @param xmlBuffer O Buffer de entrada.
* @return O documento xml
* @throws ParserConfigurationException Caso nao consiga efetuar o parser.
* @throws SAXException  Caso houver erro da biblioteca SAX
* @throws IOException Caso ouver erro de IO
*/
public Object parserXML(IoBuffer xmlBuffer) throws ParserConfigurationException, SAXException, IOException {
// captura os bits da requisicao e aloca
byte[] data = new byte[xmlBuffer.limit()];
xmlBuffer.get(data);
// transforma em string removendo espacoes em branco
String xml = new String(data).trim();
// cria o documento e retorna.
Document document = DocumentBuilderFactory.newInstance().
newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes()));
return (document);
}

}

Aqui o XMLEncoder responsável por escrever os bits para o cliente.


package br.com.ronaldorigoni.mina.codec;

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.filter.codec.textline.LineDelimiter;

/**
* Encoder de XML, responsavel por transformar a saida ao usuário e escrever a mesma.
* @author Ronaldo Rigoni
*/
public class XmlEncoder extends ProtocolEncoderAdapter {

/**
* ENCODER final
*/
private final AttributeKey ENCODER = new AttributeKey(getClass(), "encoder");

/**
* Charset da Requisição
*/
private final Charset charset;
/**
* Delimitador da requisição.
*/
private final LineDelimiter delimiter;
/**
* Tamanho máximo da requisição.
*/
private int maxLineLength = Integer.MAX_VALUE;

/**
* Construtor do Encoder
* Define o Charset e o Delimitador
*/
public XmlEncoder() {
this.charset = this.charset = Charset.forName("UTF-8");
this.delimiter = new LineDelimiter("\0");
}

/**
* Retorna o tamanho maximo da requisição.
* @return O Tamanho maximo da requisição.
*/
public int getMaxLineLength() {
return maxLineLength;
}

/**
* Codifica a saida ao Usuário.
*/
public void encode(IoSession session, Object message,ProtocolEncoderOutput out) throws Exception {
// capturando o charset da sessao.
CharsetEncoder encoder = (CharsetEncoder) session.getAttribute(ENCODER);
// caso for nulo é criado e setado na sessão do usuário.
if (encoder == null) {
encoder = charset.newEncoder();
session.setAttribute(ENCODER, encoder);
}
// mensagem a ser escrita.
// Por se tratar de XML e nao ser nada mais que uma string
// pegamos o toString() dela.
String value = message.toString();
//Cria o buffer de saida.
IoBuffer buf = IoBuffer.allocate(value.length()).setAutoExpand(true);
// adiciona ao buffer de saida a mensagem e o charset
buf.putString(value, encoder);
if (buf.position() > maxLineLength) {
throw new IllegalArgumentException("Tamanho da linha muito grande: " + buf.position());
}
// seta o delimitador \0
buf.putString(delimiter.getValue(), encoder);
// fecha o buffer
buf.flip();
// escreve na saida.
out.write(buf);
}
}

Agora criaremos nossa Factory que fabricará os objetos para as requisições.


package br.com.ronaldorigoni.mina.codec;

import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;

/**
* Classe Factory para Encoder e Decoder de XML utilizando Apache MINA
* @author Ronaldo Rigoni
*/
public class XmlProtocolCodecFactory implements ProtocolCodecFactory {

/**
* Cria um novo Encoder de XML
*/
public ProtocolEncoder getEncoder(IoSession arg0) throws Exception {
return new XmlEncoder();
}

/**
* Cria um novo Decoder de XML
*/
public ProtocolDecoder getDecoder(IoSession arg0) throws Exception {
return new XmlDecoder();
}

}

Agora vamos criar A classe de servidor, que será responsável por abrir a porta no servidor e inicializar o Handler que manipulará as requisições do usuário.


package br.com.ronaldorigoni.mina.core;

import java.net.InetSocketAddress;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.filter.logging.MdcInjectionFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

import br.com.ronaldorigoni.bingo.codec.XmlProtocolCodecFactory;
import br.com.ronaldorigoni.bingo.config.Config;

/**
* Inicializa o servidor.
*
* @author Ronaldo Rigoni
*/
public class Server {

static Logger logger = Logger.getLogger(Server.class);

private static void start(){
try {// objeto que receberá novas conecoes
NioSocketAcceptor acceptor = new NioSocketAcceptor();
// filtro de requisicoes
DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
// logger
LoggingFilter loggingFilter = new LoggingFilter();
chain.addLast("logging", loggingFilter);
// injecao do filtro
MdcInjectionFilter mdcInjectionFilter = new MdcInjectionFilter();
chain.addLast("mdc", mdcInjectionFilter);
// atribuicao do protocolo de parser de XML que criamos.
chain.addLast("codec", new ProtocolCodecFilter(new XmlProtocolCodecFactory()));
// adicionando logger.
addLogger(chain);
// adicionando  Handler, que tratará as requisiçoes
acceptor.setHandler(new ChatServerHandler());
// setando porta para ouvir.
acceptor.bind(new InetSocketAddress(8090));
logger.debug("Servidor de chat ouvindo na porta:" + 8090);
} catch (Exception e) {
logger.error("Erro ao inicializar servidor de chat.",e);
}
}
/**
* Adiciona um logger para o filtro de requisicoes.
**/
private static void addLogger(DefaultIoFilterChainBuilder chain) throws Exception {
chain.addLast("logger", new LoggingFilter());
}
/**
* Inicializa o servidor de chat.
**/
public static void main(String[] args) {
try {
start();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}

}

Agora vamos criar um objeto para encapsular as requisiçoes chamado Request, ele irá armazenar o tipo de requisiçao e os parametros contidos nela.


package br.com.ronaldorigoni.mina.codec;

import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
* Classe para encapsular uma requisição.
* @author Ronaldo Rigoni
*/
public class Request {

private static final Logger logger = Logger.getLogger(Request.class);

//Possíveis campos de um input
public static final String FIELD_USER_NAME = "userName";
public static final String FIELD_USER_LOGIN = "login";
public static final String FIELD_USER_PASSWORD = "password";
public static final String FIELD_TO = "to";
public static final String FIELD_FROM = "from";
public static final String FIELD_MESSAGE = "message";

public static final String OPERATION_CHAT_USER_IN ="add-user-in-chat-room";
public static final String OPERATION_CHAT_USER_IN ="remove-user-in-chat-room";
public static final String OPERATION_CHAT_SEND_MESSAGE ="chat-send-message";
public static final String OPERATION_XML_POLICY ="<policy-file-request/>";

private String operation;
private HashMap<String, String> parameters = new HashMap<String, String>();
private String inputText;

public Request(Document document) {
parseDocument(document);
}

/**
* @return the operation
*/
public String getOperation() {
return operation;
}

/**
* @param operation the operation to set
*/
public void setOperation(String operation) {
this.operation = operation;
}

/**
* @return the parameters
*/
public HashMap<String, String> getParameters() {
return parameters;
}

/**
* @param parameters the parameters to set
*/
public void setParameters(HashMap<String, String> parameters) {
this.parameters = parameters;
}

/**
* @return the inputText
*/
public String getInputText() {
return inputText;
}

/**
* @param inputText the inputText to set
*/
public void setInputText(String inputText) {
this.inputText = inputText;
}

private void parseDocument(Document document){
if(document == null)
throw new NullPointerException("Documento nulo.");
else{
NodeList nodes = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
if(nodes.item(i).getFirstChild() != null){
logger.debug(nodes.item(i).getNodeName()+","+ nodes.item(i).getFirstChild().getNodeValue());
parameters.put(nodes.item(i).getNodeName(), nodes.item(i).getFirstChild().getNodeValue());
}
}
if(document.getDocumentElement() != null){
this.setOperation("<"+document.getDocumentElement().getTagName()+"/>");
logger.debug("Operation:"+document.getDocumentElement().getTagName());
}
}
}

@Override
public String toString() {
return "Operation:"+getOperation()+",parameters{"+getParameters()+"}";
}
}

Agora vamos criar o Manipulador de Requisiçoes que será responsavel por tratar todas as requisições dos usuários tanto de entrada como de saida, login no chat, envio de mensagem e saida do usuáro.


package br.com.ronaldorigoni.mina.core;

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

import org.apache.log4j.Logger;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.w3c.dom.Document;

import br.com.ronaldorigoni.mina.codec.Request;

/**
* Classe manipuladora de requisiçoes, é onde toda a lógica do chat acontece.
* É nela que toda requisição após passar pelo Codec XML é encaminhado para cá/
* @author Ronaldo Rigoni rrigoni@gmail.com
*
*/
public class ChatHandler extends IoHandlerAdapter {
/** Logger estatico do Handler **/
private static final Logger logger = Logger.getLogger(ChatHandler.class);
/**
* Constante para armazenamento do usuario na sessao.
*/
private static final String USER_NAME = "username";

/**
* XML policy caso a requisicao venha de outro host diferente ao do servidor.
*/
private static final String XML_POLICY = "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"8090\"/></cross-domain-policy>";

/**
* Lista para armazenar todas as sessoes de chat.
* Esta lista necessita estar sincronizada pois em um ambiente multi-thread precisamos
* proteger acesso concorrente a mesma.
*/
private List<IoSession> sessions = Collections.synchronizedList(new ArrayList<IoSession>());

/**
* Metodo invocado quando ocorrer alguma exception na escrita de mensagens ou
* no recebimento.
* @param session Sessao do usuario.
* @param cause Exception lancada
*/
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
super.exceptionCaught(session, cause);
}
/**
* Metodo que valida um usuario baseado nos seguintes criterios para poder ingressar no chat.
* 1 - Nao pode haver a sessao do usuario na lista de chat.
* 2 - Nao pode haver uma sessao com o mesmo atributo <code>username</code> que a do usuario
* que esta tentando logar.
* @param session A sessao do usuario que requisitou login.
* @return <code>true</code> caso esta apto a ingressar no chat,
*             <code>false</code> caso nao esta apto.
*/
private boolean loginUser(IoSession session){
boolean isValid = true;
String userLogin = (String) session.getAttribute(USER_NAME);
if(userLogin != null && userLogin.length() > 5){
if(!sessions.contains(session)){
synchronized (session) {
for(IoSession ses : sessions){
// capturando login da sessao
String username = (String)session.getAttribute(USER_NAME);
if(username.equalsIgnoreCase(userLogin)){
isValid  = false;
}
}
}
}
}else{
isValid = false;
}
return isValid;
}

/**
* Invocado a cada mensagem recebida.
* Pelo fato de construirmos um Codec XML o objeto message sempre ser'a uma instancia
* de org.w3c.dom.Document, caso nao for a mensagem ser'a ignorada.
* @param session Sessao do usuario.
* @param message Mensagem recebida.
*/
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
if(message instanceof org.w3c.dom.Document){
// efetuando a conversao do documento recebido.
Document document = (Document) message;
Request request = new Request(document);
if (request.getOperation().equalsIgnoreCase(Request.OPERATION_XML_POLICY)) {
session.write(XML_POLICY);
} else if (request.getOperation().trim().equalsIgnoreCase(Request.OPERATION_CHAT_USER_IN)) {
// adiciona o username na sessao e envia para login.
session.setAttribute(USER_NAME,request.getParameters().get(USER_NAME));
boolean logged = loginUser(session);
if(!logged){
session.write("<forbiden/>");
}else{
// adiciona usuario nas sessoes do chat.
sessions.add(session);
//processa entrada do usuario.
processUserEnterInChat(session);
}
}else if(request.getOperation().trim().equalsIgnoreCase(Request.OPERATION_CHAT_USER_EXIT)){
// remove sessao do usuario da lista de sessoes do chat.
sessions.remove(session);
// processa saida do usuario
processUserExitInChat(session);
// fecha sessao do usuario.
session.close(true);
}else if(request.getOperation().trim().equalsIgnoreCase(Request.OPERATION_CHAT_SEND_MESSAGE)){
String chatMessage = request.getParameters().get(Request.FIELD_MESSAGE);
// processa mensagem de chat.
processChatMessage(chatMessage, session);
}
}else{
logger.error("Formato da requisicao invalido.");
}
}

/**
* Processa o envio de uma mensagem de chat.
* @param message Mensagem
* @param session Sessao do usuario que est'a enviando.
*/
private void processChatMessage(String message, IoSession session){
logger.debug("Usuario:"+session.getAttribute(USER_NAME)+" enviou mensagem:"+message);
writeBroadCastChatMessage(message);
}
/**
* Processa a entrada de um usu'ario no chat.
* @param session Sessao do usuario.
*/
private void processUserEnterInChat(IoSession session){
StringBuffer buff = new StringBuffer("<response>");
buff.append("<type>user-enter-in-chat</type>");
buff.append("<username>"+session.getAttribute(USER_NAME)+"</username>");
buff.append("</response>");
writeBroadCastChatMessage(buff.toString());
}
/**
* Processa a sa'ida de um usu'ario no chat.
* @param session Sessao do usuario.
*/
private void processUserExitInChat(IoSession session){
StringBuffer buff = new StringBuffer("<response>");
buff.append("<type>user-exit-in-chat</type>");
buff.append("<username>"+session.getAttribute(USER_NAME)+"</username>");
buff.append("</response>");
writeBroadCastChatMessage(buff.toString());
}

/**
* Envia uma mensagem de chat para todos os usuarios.
* @param message A mensagem.
*/
private void writeBroadCastChatMessage(String message){
synchronized (sessions) {
for(IoSession session : sessions){
session.write(message);
}
}
}
/**
* Invocao apos uma mensagem ser enviada com sucesso.
* Aqui poderemos estar efetuando um log das mensagens que foram enviadas, ou descartarmos.
* @param session Sessao de destino da mensagem
* @param message Mensagem enviada.
*/
@Override
public void messageSent(IoSession session, Object message) throws Exception {
logger.debug(session+" envia >>> "+message);
}

/**
* Metodo invocado quando a sessao do usuario for fechada, quando o socket do cliente for fechado ou
* pelo servidor de chat ou pelo cliente.
* Aqui poderemos remover atributos da sessao do usuario, ou quaisquer operacoes que precisarmos quando
* o mesmo se desconectar.
* @param session Sessao do Usuario.
*/
@Override
public void sessionClosed(IoSession session) throws Exception {
// quando conexao fechada 'e processado a saida do usuario.
logger.debug("Sessao fechada:"+session);
processUserExitInChat(session);
}

/**
* Metodo invocado quando um usuario solicitar uma conexao, (quando ela for criada).
* @param session Sessao do usuario.
*/
@Override
public void sessionCreated(IoSession session) throws Exception {
logger.debug("Sessao criada:"+session);
super.sessionCreated(session);
}

/**
* Invocado quando uma sessao entrar em timeout.
* @param session Sessao do usuario
* @param status Status da sessao.
*/
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
logger.debug("Sessao em timeout:"+session);
super.sessionIdle(session, status);
}

/**
* Metodo invocado quando uma sessao for aberta.
* @param session Sessao do usuario.
*/
@Override
public void sessionOpened(IoSession session) throws Exception {
logger.debug("Sessao aberta:"+session);
super.sessionOpened(session);
}
}

<span>

Toda a parte de servidor está concluída, em um próximo post vamos criar o cliente em Adobe Flex, e abordaremos toda a integração e funcionamento detalhado do mesmo.

Um abraço, Ronaldo.

Dúvidas? ronaldo@ronaldorigoni.com.br

[/sourcecode]
[/sourcecode]

Olá galera,

Após muitos post e tutoriais pela internet, de tanto quebrar a cabeça para gerar FLV apartir dos formatos AVI,MPEG, MPG, WMV e MOV, criei uma classe para conversão de vídeos e geração de imagens a partir do vídeo ( crop de um frame do vídeo), segue abaixo o código compartilar com vocês.

Primeiramente você precisa ter instalado em seu sistema (Linux) as seguintes bibliotecas, execute o comando abaixo como root em seu console:


sudo apt-get install libfaad-dev libmp3lame-dev libmp3lame0 libfaac-dev libfaad2-dev

Lembrando que estas bibliotecas foram instaladas em ambiente 32 bits, se você instalar em ambiente 64bits os nomes das bibliotecas podem mudar.

Só agora instale o ffmpeg:


sudo apt-get install ffmpeg

A classe abaixo utiliza o Design Pattern Singleton, onde apenas uma única instância do conversor de vídeos ficará ativa por se tratar de um processo que utiliza muitos recursos do sistema operacional foi optado por criar uma Thread interna que converterá um único vídeo por vez, os restantes ficarão na fila de espera.

Segue abaixo classe em questão, lembrando que não foi abordado aqui o JavaBean Midia, que contem apenas dois atributos "caminho" e "destino" com seus respectivos getters e setters. Crie você mesmo este bean e modifique a lista de pool de vídios a converter.

package br.com.ronaldorigoni.videoconverter.util;

import br.gov.mec.portaldoprofessor.client.cms.JornalRemote;
import br.gov.mec.portaldoprofessor.client.vo.MidiaAProcessar;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.naming.InitialContext;
import org.apache.log4j.Logger;

/**
* Classe utilitária para conversão de vídeos usando a biblioteca natíva <code>ffmpeg</code> * Utiliza o mecanismo de pool de conversão.
* Para um correto funcionamento deve-se instalar a biblioteca com as seguintes dependências.
* ffmpeg - A própria biblioteca
* libfaad-dev - Biblioteca de desenvolvimento de áudio é vídeo.
* libfaac-dev  - Biblioteca de desenvolvimento para otimizaçãoes no streaming de áudio.
* libfaad2-dev - Biblioteca de desenvolvimento de áudio e vídeo versão dois.
* Testado em ambiente Ubuntu - Debian
* @author Ronaldo Rigoni
*/
public class VideoConverter {

/**
* Logger statico para o Conversor de Vídeo
*/
private static final Logger logger = Logger.getLogger(VideoConverter.class);
/**
* Pool de mídias a converter.
*/
private static final List<Midia> midiasAProcessar = new ArrayList<Midia>();

/**
* Adiciona uma midia no pool.
* @param midia Midia a converter.
*/
public void addMediaInPool(MidiaAProcessar midia) {
midiasAProcessar.add(midia);
}
/**
* Thread responsável por verificar a todo momento se existem vídeos a serem convertidos
* Caso possuir algum vídeo, efetua a conversão gera as imágens do respectivo vídeo,
* atualiza em banco as informaçãoes da mídia e notifica ao usuário que inseriu este vídeo.
*/
private Runnable worker = new Runnable() {
public void run() {
while (true) {
if (!midiasAProcessar.isEmpty() &amp;amp;amp;&amp;amp;amp; midiasAProcessar.get(0) != null) {
MidiaAProcessar midiaAProcessar = midiasAProcessar.get(0);
String command = command(midiaAProcessar.getCaminho(), midiaAProcessar.getDestino());
String commandCropImagemMedia = commandImagemMedia(midiaAProcessar.getDestino());
String commandCropImagemMicro = commandImagemMicro(midiaAProcessar.getDestino());
Process videoProcess = null;
Process imageMediaProcess = null;
Process imageMicroProcess = null;
try {
videoProcess = Runtime.getRuntime().exec(command);
int code = videoProcess.waitFor();
if (code == 0) {
logger.debug("Gerado video com sucesso.");
} else {
logger.error("Erro ao converter video");
processConsoleOut(videoProcess.getErrorStream());
}
if (code == 0) {
imageMediaProcess = Runtime.getRuntime().exec(commandCropImagemMedia);
int imageMediaCode = imageMediaProcess.waitFor();
if (imageMediaCode == 0) {
logger.debug("Imagem media gerada com sucesso.");
} else {
logger.error("Erro ao gerar imagem media." );
processConsoleOut(imageMediaProcess.getErrorStream());
}
}
logger.debug(" Executando:"  + commandCropImagemMicro);
if (code == 0) {
imageMicroProcess = Runtime.getRuntime().exec(commandCropImagemMicro);
int imageMicroCode = imageMediaProcess.waitFor();
if (imageMicroCode == 0) {
logger.debug(" Imagem micro gerada com sucesso." );
} else {
logger.error(" Erro ao gerar imagem micro." );
processConsoleOut(imageMicroProcess.getErrorStream());
}
}
// codigo de saida igual a zero significa sucesso.
if (code == 0) {
// aqui voce pode notificar o usuário que solicitou ao conversão do vídeo
// de que o mesmo ja se encontra disponível, ou tratar de alguma forma
// a conversao com sucesso.
}
} catch (Exception e) {
e.printStackTrace();
}
midiasAProcessar.remove(0);
}
}
}
};
private static VideoConverter instance;

private VideoConverter() {
init();
}

/**
* Inicializa o mecanizmo de conversão de videos.
*/
private void init() {
new Thread(worker).start();
logger.info(" Mecanizmo de vídeos inicializado com sucesso." );
}

/**
* Retorna uma instância singleton do conversor de vídeos.
* @return Instancia singleton VideoConverter
*/
public static synchronized VideoConverter getInstance() {
if (instance == null) {
instance = new VideoConverter();
}
return instance;
}
/**
* Formato MPEG
*/
public static final String MPEG_FORMAT = " mpeg";
/**
* Formato MPG
*/
public static final String MPG_FORMAT = "mpg";
/**
* Formato AVI
*/
public static final String AVI_FORMAT = "avi";
/**
* Formato MOV
*/
public static final String MOV_FORMAT = "mov";

/**Formato WMV **/
public static final String WMV_FORMAT = "wmv";
/**
* Representa o comando para conversão de vídeos AVI
* {0} = Caminho completo do arquivo em disco ( vídeo de entrada).
* {1} = Caminho completo com extenção do arquivo de saída.
* Importante: para otimizar qualidade apenas modifique os parâmetros -qmin e -qmax
* -qmin 0 a 30
* -qmax 0 a 30
* Em todas as hipotezes -qmax deve ser um valor maior que -qmin, -qmin reduz o tamanho do video
* e -qmax assegura manter a qualidade, portando deve ser alto e alterar prefeferivelmente -qmin.
*/
private static final String COMMAND_AVI = "ffmpeg -i   {0}  -qmin 6 -qmax 26 -ar 22050 -f flv -y  {1}";
/**
* Representa o comando para converter um vídeo no formato MOV para FLV.
* {0} = Caminho completo do arquivo em disco ( vídeo de entrada).
* {1} = Caminho completo com extenção do arquivo de saída.
* Apenas altere os parâmetros -qmin e -qmax.
* -qmin 0 a 30
* -qmax 0 a 30
* -s significa o tamanho da saida do vídeo, resolução.
*/
private static final String COMMAND_MOV = " ffmpeg -y -i  {0}  -vcodec flv -b 400kb -s 500x376 -qmin 10 -qmax 30 -ar 22050 -ac 1  {1} ";
/**
* Representa o comando para converter um vídeo no formato MPG ou MPEG para FLV.
* {0} = Caminho completo do arquivo em disco ( vídeo de entrada).
* {1} = Caminho completo com extenção do arquivo de saída.
* -r frames por segundo
* -s tamanho de saida do vídeo.
*/
private static final String COMMAND_MPG_MPEG = " ffmpeg -i  {0}  -y -r 25 -b 500k -ar 22050 -s 500x376 -ab 24k  {1}";
/**
* Representa o comando para efetuar o crop da imagem media de um vídeo.
* {0} = Caminho completo do arquivo em disco ( vídeo de entrada).
* {1} = Caminho completo com extenção do arquivo de saída.
* -itsoffset representa o segundo em que será efetuado o crop
* -vframes significa o respectivo frame para o segundo expecificado acima
* -s tamanho da imagem. Só aceita medidas pares.
*/
private static final String COMMAND_CROP_VIDEO_IMAGEM_MEDIA = "ffmpeg  -itsoffset -10  -i  {0}  -vcodec mjpeg -vframes 1 -an -f rawvideo -s 130x96 -y {1}";
/**
* Representa o comando para efetuar o crop da imagem pequena de um vídeo.
* {0} = Caminho completo do arquivo em disco ( vídeo de entrada).
* {1} = Caminho completo com extenção do arquivo de saída.
* -itsoffset representa o segundo em que será efetuado o crop
* -vframes significa o respectivo frame para o segundo expecificado acima
* -s tamanho da imagem. Só aceita medidas pares.
*/
private static final String COMMAND_CROP_VIDEO_MICRO_IMAGEM = "ffmpeg  -itsoffset -10  -i  {0}  -vcodec mjpeg -vframes 1 -an -f rawvideo -s 60x46 -y {1}";

/**
* Representa comando de conversão de videos WMV para FLV.
* {0} = Caminho completo do arquivo em disco ( vídeo de entrada).
* {1} = Caminho completo com extenção do arquivo de saída.
* -s = Tamanho do vídeo ( resolução), só aceita medidas pares.
* -qmin e -qmax são parâmetros de qualidade.
* -qmin 0 a 30
* -qmax 0 a 30
*/
private static final String COMMAND_WMV = "ffmpeg -y -i {0} -ar 22050 -ab 32 -s 500x376 -qmin 6 -qmax 26 -f flv -s 320x320 {1}";

/**
* Gera o comando expecífico de acordo com a extenção do arquivo.
* @param videoIn Caminho de entrada do vídeo.
* @param videoOut Caminho de saída do video.
* @return O comando formatado.
*/
private static String command(String videoIn, String videoOut) {
String command = &amp;amp;quot;&amp;amp;quot;;
String fileName = videoIn.substring(videoIn.lastIndexOf("/") + 1, videoIn.length());
String extension = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
// formato AVI
if (extension.equalsIgnoreCase(AVI_FORMAT)) {
command = new MessageFormat(COMMAND_AVI).format(new Object[]{videoIn, videoOut});
// formato MOV
} else if (extension.equalsIgnoreCase(MOV_FORMAT)) {
command = new MessageFormat(COMMAND_MOV).format(new Object[]{videoIn, videoOut});
// formato MPEG ou MPG
} else if (extension.equalsIgnoreCase(MPEG_FORMAT) || extension.equalsIgnoreCase(MPG_FORMAT)) {
command = new MessageFormat(COMMAND_MPG_MPEG).format(new Object[]{videoIn, videoOut});
// formato WMV
}else if(extension.equalsIgnoreCase(WMV_FORMAT)){
command = new MessageFormat(COMMAND_WMV).format(new Object[]{videoIn, videoOut});
}else {
throw new IllegalArgumentException("Formato de arquivo não suportado:"+extension);
}
return command;
}

/**
* Processa o stream de saída de erros caso alguma conversão não tenha ocorrido com sucesso.
* @param is InputStream
* @throws java.io.IOException Em caso de excessão de IO
*/
private void processConsoleOut(InputStream is) throws IOException {
byte[] buffer = new byte[512];
int bytes = 0;
StringBuffer stringBuffer = new StringBuffer();
do {
buffer = new byte[512];
bytes = is.read(buffer);
if (bytes > 0) {
stringBuffer.append(new String(Arrays.copyOf(buffer, bytes)));
}
} while (bytes >= buffer.length);
String output = stringBuffer.toString();
logger.error(output);
}

/**
* Gera o comando de crop para uma imagem tamanho médio.
* IMPORTANTE: Altere o tamanho para geraçao da sua imagem média para -s alturaXlargura
* @param videoOut Caminho do vídeo.
* @return Comando formatado para crop de imagem média.
*/
private static String commandImagemMedia(String videoOut) {
String imagenOut = videoOut + ".media.jpeg";
String commandImagemMedia = new MessageFormat(COMMAND_CROP_VIDEO_IMAGEM_MEDIA).format(new Object[]{videoOut, imagenOut});
return commandImagemMedia;
}

/**
* Gera o comando de crop para uma imagem micro.
* @param videoOut O caminho completo do vídeo de entrada.
* @return O Comando de crop de imagem micro formatado.
*/
private static String commandImagemMicro(String videoOut) {
String imageOut = videoOut + ".micro.jpeg";
String commandImagemMicro = new MessageFormat(COMMAND_CROP_VIDEO_MICRO_IMAGEM).format(new Object[]{videoOut, imageOut});
return commandImagemMicro;
}

}

Espero ajudar de alguma forma e que você tenha sucesso em suas conversões de vídeos.

Qualquer dúvida ronaldo at ronaldorigoni.com.br

Buenas galera.

Quem nunca passou pelo trabalho de efetuar Merge entre versões de projeto em um repositório de um controlador de versão? Uma tarefa muitas vezes árdua principalmente se quanti precisamos unir alterações entre dois branches em um só.

Vou postar aqui uma ferramenta livre desenvolvida em Python para Linux chamada MELD que efetua a comparação entre dois repositórios ou dois arquivos, há a possibilidade de se comparar até tres diretórios ou arquivos ao mesmo tempo e mesclar as alterações em uma versão apenas.

Primeiramente vamos istalar a ferramenta para isso abra seu console e digite o comando abaixo:


sudo apt-get install meld

Após o processo de instalação ter sido concluido, abra a ferramenta com o comando:


sudo meld

Porque abrir como sudo?

Se você tiver os repositórios controlados pelo Subversion ou CVS, necessitará de permissão de root para alterar os mesmos.

Iremos demonstrar o trabalho com merge em um projeto com varios arquivos, seleciona os diretorios conforme imagem abaixo:

selecao_projetos

2

Apos isso quando clicado em OK, o MELD irá fazer uma análise de todos os arquivos e diretorois dos dois projetos e irá nos expor o resultado.

Os arquivos marcados em vermelho são arquivos com conflito que precisam ser resolvidos, os arquivos marcados em verde são arquivos novos, onde este arquivo está em um projeto e em outro não.

3

Para resolver um conflito entre dois arquivos basta dar um duplo clique sobre o mesmo que o Meld mostrará um editor onde você pode efetuar as alterações entre os dois arquivos a fim de deixá-los iguais conforme tela abaixo:

4

Para jogar alterações de um lado para o outro basta clicar na seta do texto marcado em verde.

5

Quando o merge estiver concluido dos dois arquivos basta salvar ambos e fechar o editor de comparação de arquivos.

Para arquivos novos marcados em verde, basta clicar sobre o mesmo com o botão direito e selecionar  "Copy to Left" ou "Copy to Right",

e o Meld irá copiar o arquivo para o outro projeto.

6

Para copiar diretórios inteiros basta efetuar o mesmo passo que adotado para arquivos.

O Meld mostra toda a estrutura de arquivos e diretórios que possuirem alguma diferença com o outro projeto comparado.

7Após concluir todo o processo basta fechar o Meld e abrir seu projeto com sua IDE, e efetuar o commit no repositório.

Importante: O Meld não altera os arquivos de controle do seu gerenciador de versão, então quando sincronizado com seu repositório o SNV ou CVS identificará as alterações nos arquivos e você poderá efetuar o commit normalmente. O Meld não trabalha necessáriamente com projetos ou arquivos que estejam em seu gerenciador de versão.

Ferramenta simples mas de grande ajuda em caso de grandes Merges e Diffs, ja salvou minha pele várias vezes.

Espero que possa ajudar você de alguma forma.

Abraços e até um próximo post, dúvidas? ronaldo@ronaldorigoni.com.br

Per copiare tutta la directory solo per fare lo stesso passo che usate per.

Salve galera.

Hoje vamos abordar uma forma simples de trabalhar com MVC no Flex com Eventos.

Primeiramente oque é MVC?

Com o aumento da complexidade das aplicações desenvolvidas torna-se fundamental a separação entre os dados (Model) e o layout (View). Esta forma permite que trabalhamos separadamente entre layout e manipulação de dados, tornando flexível alterações no layout sem afetar o Controller e vice-versa.

O MVC resolve um dos maiores problemas entre acoplamento das camadas de acesso a dados e lógica de negócio da aplicação e apresentação de dados ao usuário, pois introduz um componente entre as duas camadas (dados e interface) chamada Controller, o Design Patter MVC está mais ligado a arquitetura da aplicação do que um tipo de padrão de projeto, pois afeta a maneira de como a aplicação será organizada e estruturada.

O MVC não abrange a camada de acesso a dados, pois supõe-se que ela esteja encapsulada dentro do model.

OBS: Camadas dizem como separar os componentes, mas MVC diz como os componentes interagem entre si.

As camadas:

  • Model: A representação especifica da informação em que a aplicação trabalha. Por exemplo, cliente, fornecedor e produtos fazem parte do domínio de um sistema de PDV. Não confunda Model com outro nome da camada de dominio. Lógica de dominio apresenta traz maior sentido aos dados crus. Ex: Temos um cliente com um saldo de crédito X, e um histórico de compras e pagamentos, e criamos um método que percorre o histórico de compras do cliente e de pagamentos e nos devolve se podemos ou não aumentar o crédito do cliente, isso é chamada lógida de domínio, onde é agregado maior sentido aos dados propriamente ditos "crus".
  • View: A camada view é responsável por expor os dados a uma iteração com o usuário lendo do Model as informações necessárias.
  • Controller: Controller é onde ocorre todo o processamento, validações, manipulação de eventos e solicitações do usuário.

O diagrama representa as seguintes associações:

  • O controller conhece a view e o model.
  • A view conhece apenas o model.
modelviewcontrollerdiagram

Modelo MVC

Fluxo:

  1. Ocorre uma interação do usuário com a interface ( clique em um botão de listagem).
  2. O controller recebe a solicitação pois havia sido previamente definido em uma rotina a manipulação do evento no proprio controller.
  3. O controller acessa o model baseado na interação do usuário.
  4. A view utiliza o model para gerar a interface( na maioria dos casos exibir os dados ou requisições do usuário),  a view obtem os dados do model sem que ele tenha conhecimento da camada de apresentação.
  5. A view espera as próximas interações do usuário e o ciclo se inicia.

Bem vamos a parte prática:

Crie a seguinte estrutura de pacotes:

pacotes_exemplo_flex

Agora vamos criar os componentes MXML dentro do pacote view:


&amp;lt;?xml version=&amp;amp;quot;1.0&amp;amp;quot; encoding=&amp;amp;quot;utf-8&amp;amp;quot;?&amp;gt;
&amp;lt;mx:Panel title=&amp;amp;quot;Listagem&amp;amp;quot; creationComplete=&amp;amp;quot;init()&amp;amp;quot; xmlns:mx=&amp;amp;quot;http://www.adobe.com/2006/mxml&amp;amp;quot; width=&amp;amp;quot;400&amp;amp;quot; height=&amp;amp;quot;300&amp;amp;quot;&amp;gt;
&amp;lt;mx:List id=&amp;amp;quot;list&amp;amp;quot; width=&amp;amp;quot;100%&amp;amp;quot; dataProvider=&amp;amp;quot;{model.clients}&amp;amp;quot; labelField=&amp;amp;quot;name&amp;amp;quot; height=&amp;amp;quot;100%&amp;amp;quot; /&amp;gt;
&amp;lt;mx:ApplicationControlBar&amp;gt;
&amp;lt;mx:Button id=&amp;amp;quot;btList&amp;amp;quot; label=&amp;amp;quot;Listar&amp;amp;quot; /&amp;gt;
&amp;lt;mx:Button id=&amp;amp;quot;btClear&amp;amp;quot; label=&amp;amp;quot;Limpar&amp;amp;quot; /&amp;gt;
&amp;lt;/mx:ApplicationControlBar&amp;gt;
&amp;lt;mx:Script&amp;gt;
&amp;lt;![CDATA[
import br.com.ronaldorigoni.controller.ListagemController;
import br.com.ronaldorigoni.model.ListagemModel;
[Bindable]
public var model:ListagemModel = null;
public var controller:ListagemController = null;

private function init():void{
model = new ListagemModel();
controller = new ListagemController(this);
}
]]&amp;gt;
&amp;lt;/mx:Script&amp;gt;
&amp;lt;/mx:Panel&amp;gt;

Sublistagem.mlxml


&amp;lt;?xml version=&amp;amp;quot;1.0&amp;amp;quot; encoding=&amp;amp;quot;utf-8&amp;amp;quot;?&amp;gt;
&amp;lt;mx:Panel title=&amp;amp;quot;Sublistagem&amp;amp;quot; creationComplete=&amp;amp;quot;init()&amp;amp;quot; xmlns:mx=&amp;amp;quot;http://www.adobe.com/2006/mxml&amp;amp;quot; width=&amp;amp;quot;400&amp;amp;quot; height=&amp;amp;quot;300&amp;amp;quot;&amp;gt;
&amp;lt;mx:Form&amp;gt;
&amp;lt;mx:FormItem label=&amp;amp;quot;ID:&amp;amp;quot;&amp;gt;
&amp;lt;mx:TextInput editable=&amp;amp;quot;false&amp;amp;quot; id=&amp;amp;quot;clientId&amp;amp;quot; text=&amp;amp;quot;{model.client.id}&amp;amp;quot; /&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;mx:FormItem label=&amp;amp;quot;Name&amp;amp;quot;&amp;gt;
&amp;lt;mx:TextInput editable=&amp;amp;quot;false&amp;amp;quot; id=&amp;amp;quot;clientName&amp;amp;quot; text=&amp;amp;quot;{model.client.name}&amp;amp;quot;/&amp;gt;
&amp;lt;/mx:FormItem&amp;gt;
&amp;lt;/mx:Form&amp;gt;
&amp;lt;mx:Script&amp;gt;
&amp;lt;![CDATA[
import br.com.ronaldorigoni.controller.SublistagemController;
import br.com.ronaldorigoni.model.SublistagemModel;
[Bindable]
public var model:SublistagemModel = null;
public var controller:SublistagemController = null;

private function init():void{
model = new SublistagemModel();
controller = new SublistagemController(this);
}
]]&amp;gt;
&amp;lt;/mx:Script&amp;gt;
&amp;lt;/mx:Panel&amp;gt;

Em seguida criaremos os controllers para cada componente da camada view:

ListagemController.as


package br.com.ronaldorigoni.controller{
import br.com.ronaldorigoni.event.ListagemEvento;
import br.com.ronaldorigoni.view.Listagem;
import br.com.ronaldorigoni.vo.Client;

import flash.events.MouseEvent;

import mx.collections.ArrayCollection;
import mx.events.ListEvent;

public class ListagemController{
// instancia da camada view para o controller poder manipulá-la
private var view:Listagem = null;
/**
* Contrutor do controller
* @param _view A instancia da view
*/
public function ListagemController(_view:Listagem){
// atribue a instancia da view a instancia local
this.view = _view;
// adiciona evento de clique para listar clientes
view.btList.addEventListener(MouseEvent.CLICK, listClients);
// adiciona evento para limpar a listagem de clientes
view.btClear.addEventListener(MouseEvent.CLICK, clearClients);
// adiciona evento de selecao de um cliente
this.view.list.addEventListener(ListEvent.ITEM_CLICK, dispatchEvent);
}
/**
* Invoca preenchimento do modelo de clientes.
* Lembrando que em uma aplicacao real isso deve ser
* feito por uma linguagem de servidor, geralmente com acesso
* a banco de dados.
*/
private function listClients(event:MouseEvent):void{
fillClients();
}

/**
* Limpa a lista de clientes.
*/
private function clearClients(event:MouseEvent):void{
view.model.clearModel();
}

/**
* Preenche a lista de clientes.
* Sendo quem em uma aplicação real estes dados devem ser alimentados
* por uma linguagem de servidor.         *
*/
private function fillClients():void{
for(var i:int = 0; i &amp;lt; 10; i ++){
var client:Client = new Client();
client.name = &amp;amp;quot;Client &amp;amp;quot;+i;
client.id = i;
view.model.addClient(client);
}
}

/**
* Dispara um evento para o componente pai da listagem, e este
* terá adicionado os listeners para este tipo de evento.
* Onde fará a invocação das funcoes.
*/
private function dispatchEvent(event:ListEvent):void{
// setando o cliente selecionado dentro do modelo de listagem
view.model.selected = view.list.selectedItem as Client;
var listagemEvento:ListagemEvento =    new ListagemEvento(ListagemEvento.EVENT_CLIENT_SELECTED);
// inserindo dentro do objeto de evento o cliente selecionado.
listagemEvento.sublistagemModel.client = view.model.selected;
// invoca o componente pai da view no caso a application
// para disparar um evento, sendo que nela esta definido um ouvinte para este
// tipo de evento, e a funcao será invocada, internamente no evento existe os dados
// que a visão necessita para exibir.
view.parent.dispatchEvent(listagemEvento);
}
}
}

SublistagemController.as


package br.com.ronaldorigoni.controller{
import br.com.ronaldorigoni.event.ListagemEvento;
import br.com.ronaldorigoni.view.Sublistagem;

import mx.controls.Alert;
public class SublistagemController{

/**
* Instancia da camada view
*/
private var view:Sublistagem = null;

/**
* Construtor do controller, recebe instancia da camada view para
* poder manipulá-la
*/
public function SublistagemController(_view:Sublistagem){
view = _view;
}
/**
* Manipula o evento de cliente selecionado.
*/
public function handleEventSelected(event:ListagemEvento):void{
// seta o titulo do painel de sublistagem
view.title = &amp;amp;quot;Sublistagem       Evento recebido:&amp;amp;quot;+event.type;
// seta o model da sublistagem
// quando o model é setado ele é automaticamente renderizado
// na view, pois está marcado com o [Bindable]
view.model = event.sublistagemModel;
}
}
}

Agora vamos criar as Classes de Modelo para os dois componentes visuais da camada View:

ListagemModel.as


package br.com.ronaldorigoni.model{
import br.com.ronaldorigoni.vo.Client;

import mx.collections.ArrayCollection;

public class ListagemModel{
/**
* Array de clientes que será exibido na listagem.
*/
private var _clients:ArrayCollection = new ArrayCollection();
private var _selected:Client;

public function ListagemModel(){
}
/**
* Defina o arrayCollection de clientes.
*/
public function set clients(clients:ArrayCollection):void{
this._clients = clients;
}
/**
* Retorna o arrayCollection de clientes
*/
public function get clients():ArrayCollection{
return _clients;
}
/**
* Limpa o array de clientes.
*/
public function clearModel():void{
this._clients.removeAll();
}
public function addClient(client:Client):void{
_clients.addItem(client);
}

public function set selected(selected:Client):void{
this._selected = selected;
}
public function get selected():Client{
return _selected;
}
}
}

E SublistagemModel.as


package br.com.ronaldorigoni.model{
import br.com.ronaldorigoni.vo.Client;

public class SublistagemModel{

/**
* Representa o cliente atualmente selecionado.
*/
private var _client:Client = new Client();

public function SublistagemModel(){
}

/**
* Seta o cliente atualmente selecionado.
*/
public function set client(client:Client):void{
this._client = client;
}
/**
* Recupera o cliente atual selecionado.
*/
public function get client():Client{
return _client;
}
}
}

Agora vamos criar a classe de Eventos que armazenará uma instância de SublistagemModel para notificar o controller de Sublistagem.

&amp;lt;/pre&amp;gt;
package br.com.ronaldorigoni.event{
import br.com.ronaldorigoni.model.SublistagemModel;

import flash.events.Event;
/**
* Classe de eventos para operacoes de listagem.
* Armazena o cliente atual selecionado.
*/
public class ListagemEvento extends Event{

/**
* Representa um evendo de cliente selecionado.
*/
public static const EVENT_CLIENT_SELECTED:String = &amp;amp;quot;clientSelected&amp;amp;quot;;

private var _sublistagemModel:SublistagemModel = new SublistagemModel();
public function ListagemEvento(tipo:String){
super(tipo,true);
}
/**
* Define o modelo selecionado a sublistagem
*/
public function set sublistagemModel(sublistagemModel:SublistagemModel):void{
this._sublistagemModel = sublistagemModel;
}
/**
* Recupera o modelo da sublistagem
*/
public function get sublistagemModel():SublistagemModel{
return _sublistagemModel;
}
}
}

Esta classe de evento mantem uma propriedade do tipo cliente que alimentará o model de Sublistagem.

Agora vamos criar a classe de VO Client.as, que armazena as informações de cada cliente.


package br.com.ronaldorigoni.vo{
/**
* VO de cliente.
*/
public class Client{

private var _id:uint;
private var _name:String;

public function Client(){
}
/**
* seta o nome do cliente
*/
public function set name(name:String):void{
this._name = name;
}
/**
* Seta o id do cliente;
*/
public function set id(id:uint):void{
this._id = id;
}
/**
* Retorna o atual nome do cliente
*/
public function get name():String{
return _name;
}
/**
* Retorna o id do cliente
*/
public function get id():uint{
return _id;
}
}
}

E por fim nosso código do nosso application ExemploMVC.mxml:


&amp;lt;?xml version=&amp;amp;quot;1.0&amp;amp;quot; encoding=&amp;amp;quot;utf-8&amp;amp;quot;?&amp;gt;
&amp;lt;mx:Application creationComplete=&amp;amp;quot;init()&amp;amp;quot; xmlns:mx=&amp;amp;quot;http://www.adobe.com/2006/mxml&amp;amp;quot; layout=&amp;amp;quot;horizontal&amp;amp;quot; xmlns:view=&amp;amp;quot;br.com.ronaldorigoni.view.*&amp;amp;quot;&amp;gt;
&amp;lt;mx:HBox&amp;gt;
&amp;lt;view:Listagem id=&amp;amp;quot;listagem&amp;amp;quot;/&amp;gt;
&amp;lt;view:Sublistagem id=&amp;amp;quot;sublistagem&amp;amp;quot;/&amp;gt;
&amp;lt;/mx:HBox&amp;gt;
&amp;lt;mx:Script&amp;gt;
&amp;lt;![CDATA[
import br.com.ronaldorigoni.event.ListagemEvento;
/**
* Funcao é disparada quando a aplicacao terminar de carregar.
*/
private function init():void{
/**
* Adiciona o listener de evento, para quando for disparado um
* evento deste tipo a funcao para manipular seja invocada.
*/
addEventListener(ListagemEvento.EVENT_CLIENT_SELECTED,
sublistagem.controller.handleEventSelected);
}
]]&amp;gt;
&amp;lt;/mx:Script&amp;gt;
&amp;lt;/mx:Application&amp;gt;

No application tem um listener adicionado para quando for disparado um evento do tipo "ListagemEvento.EVENT_CLIENT_SELECTED", que dispara a função "SublistagemConroller.handleEventSelected", que captura dentro do evento o cliente selecionado atravéz da propriedade "selected", e atualiza o modelo de sublistagem fazendo com que o cliente selecionado chege até a sublistagem sem que os componentes esteja acoplados.

Para testarmos o funcionamento rode a aplicação como Flex Application, clique sobre "Listar", onde o controller manipula este clique e lista os clientes, quando for clicado sobre um cliente na List da Listagem.mxml será criado uma instância de ListagemEvento e adicionado o Client selecionado dentro deste evento e é disparado através do componente pai de Listagem.xml o evento criado, como temos um listener para este tipo de evento em ExemploMVC.mxml ele dispara a função com este evento como um parâmetro, e la é feita a manipulação e atualização do model de Sublistagem.

A grande vantagem de se programar desta forma é que os componentes não ficam acoplados e não se conhecem em tempo de compilação. Já esta estrutura de MVC permite que possamos futuramente efetuar alterações em cada camada sem que outra camada seja afetada ou alterado o funcionamento. Ex, se precisarmos alterar o layout da aplicação o controller não precisa saber disso.

Com o uso da anotação de metadados "Bindable", nos modelos dentro da view, faz com que qualquer alteração pelo Controller sobre o model ela seja automaticamente exibida na camada view, e no caso inverso, quando o usuário altera algum dado apresentado na camada view, ele já é autimaticamente atualizado no model, sendo assim o controller tem total controle sobre a exibição e alteração dos dados.

Aparentemente com esta estrutura temos mais código, mas isso é relativamente incomparável quando for um sistema relativamente grande e que necessite de constante manutenção, pois será alterado apenas as partes que necessitam sem afetar as demais.

Todas as classes estão comentadas e explicadas, qualquer dúvida crítica ou sugestão me escreva ronaldo arroba ronaldorigoni.com.br

Segue link para download do projeto.

Abraços e até um próximo post.

Monday, May 25, 2009Google Developers Day 2009

Ola galera,

Lançameto do Google Developer Day, acontecerá em SP dia 29/06. Para maiores informações e inscrições acesse http://code.google.com/intl/pt-BR/events/developerday/2009/about.html

Uma das palestras ocorridas em 2008, outras palestras estão disponíveis em http://www.youtube.com/googlebrasil .

vagas limitadas e inscrição gratuita. Eu vou.

e você?

Ronaldo

Friday, May 15, 2009Fábrica de RemoteObjetc em Flex

Ola pessoal.

Exemplo simples de criação de uma fábrica de RemoteObject em ActionScript 3, que utiliza por base função de ResultEvent e FaultEvent e como configuração uma classe de constantes para o RemoteObject testada e usada com o framework Java Blaze Data Services.
Dica rápida…

OBS: Classe testada e utilizada com BlazeDs.


package br.com.ronaldorigoni.remote{
import mx.controls.Alert;
import mx.core.Application;
import mx.managers.CursorManager;
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.rpc.AbstractOperation;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.mxml.RemoteObject;

/**
* Classe de requisições remotas usando RemoteObject.
* @autor Ronaldo Rigoni.
**/
public class RemoteCall{

/**
* Indica se o channelSet está construido.
*/
private var channelBuilded:Boolean = false;
/**
* ChannelSet do RemoteObject
**/
private var channelSet : ChannelSet = new ChannelSet();
/**
* RemoteDestination
**/
private var destination:RemoteDestination = null;

/**
* Função chamada quando tudo ocorrer corretamente.
*/
private var functionSuccess:Function = null;

/**
* Função chamada quando algo de errado ocorrer
*/
private var functionError:Function = null;

/**
*
* @param destination O Destination do RemoteObject.
**/
public function RemoteCall(destination:RemoteDestination){
this.destination = destination;
}

/**
* Cria um ChannelSet.
**/
private function getChannelSet():void{
var amfChannel:AMFChannel = new AMFChannel("my-amf",getMessageBroker());
channelSet.addChannel(amfChannel);
channelBuilded = true;
}
/**
* Retorna o endereço messagebroker da aplicação.
**/
private function getMessageBroker():String{
return Application.application.url.substr(0, Application.application.url.indexOf("/cms.swf"))+"/messagebroker/amf";
}

/**
* Invoca uma requisição remota.
* @param operation Operação a ser executada no lado servidor.
* @param faultFunction Função a ser executada caso ocorra um erro.
* @param successFunction Função a ser executada caso a operação remota ocorra com sucesso.
* @param args Argumentos passados a operação remota.
**/
public function invoke(operation:String, faultFunction:Function = null,
successFunction:Function = null, ...args):void{
functionSuccess = successFunction;
functionError = faultFunction;
// capturando o remoteObject
var remoteObject : RemoteObject = getRemoteObject(destination.getDestination());
// setando o source ( pacote + classe )
remoteObject.source = this.destination.getSource();
// adicionando listener para a funcao de retorno
remoteObject.addEventListener(ResultEvent.RESULT, onSuccess);
//se foi definido uma função de erro.
remoteObject.addEventListener(FaultEvent.FAULT, onFault);
// se a lista de argumentos for vazia não será passado nada como parametro.
// capturando o nome do método do destination a ser invocado.
var remoteOperation:mx.rpc.AbstractOperation = remoteObject[operation];
if(args[0] != null){
// serando os argumentos
remoteOperation.arguments = args;
}
CursorManager.setBusyCursor();
// invocando o servico remoto
remoteOperation.send();
}
/**
* Constroe o remoteObject
**/
private function getRemoteObject(destination:String):RemoteObject{
if(channelBuilded == false)
getChannelSet();
var remoteObject:RemoteObject = new RemoteObject(destination);
remoteObject.channelSet = channelSet;
return remoteObject;
}

private function onSuccess(result:ResultEvent):void{
CursorManager.removeBusyCursor();
if(this.functionSuccess != null){
this.functionSuccess.call(this, result);
}
}

/**
* Manipulador default de mensagens de erro, caso não for
* expecificado será invocada esta função em caso de erro.
**/
private function onFault(result:FaultEvent):void{
CursorManager.removeBusyCursor();
if(this.functionError != null){
this.functionError.call(this, result);
}else{
Alert.show(String(result.message));
}
}
}
}

Toda a classe está comentada e de fácil entendimento.

A RemoteCall recebe como parâmetro um argumento do tipo RemoteDestination conforme abaixo, onde é configurado como constantes um "destination", que possue o source e o id do serviço remoto.


package br.com.ronaldorigoni.remote{
/**
* Classe de constantes para declarações de RemoteDestination,
* onde contém as informações de source e id do destination em questão.
**/
public class RemoteDestination{
private var destination : String = new String();
private var source:String = new String();
/**
* Nome de destination para operações com Usuário.
**/
public static const USER_DESTINATION:RemoteDestination = new RemoteDestination                        ("userDestination","br.com.ronaldorigoni.destinations.UserDestination";);

public function RemoteDestination(name:String, source:String){
this.destination = name;
this.source = source;
}
/**
* Retorna o id do serviço remoto.
**/
public function getDestination():String{
return destination;
}
/**
* Retorna o source do serviço remoto.
**/
public function getSource():String{
return this.source;
}
}
}

E para utilizarmos é simples basta intanciarmos conforme abaixo:


private var remoteObject:RemoteCall = new RemoteCall(RemoteDestination.USER_DESTINATION);

remoteObject.invoke("nomeDoMetodoNoDestination",minhaFuncaoDeErro,minhaFuncaoDeSucesso,null);

Qualquer dúvidas ronaldo@ronaldorigoni.com.br

Até um próximo post.

Get Adobe Flash playerPlugin by wpburn.com wordpress themes

© 2007 Ronaldo Rigoni | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress