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

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]

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.

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.

Olá galera,
Qual desenvolvedor Java e Flex não se deparou com a sintaxe estranha do texto produzido pelo RichTextEditor contido no Adobe Flex?

Segue abaixo exemplo simples utilizando Expressões Regulares em java de como remover esta formatação transformando o texto em XHTML 4.1 válido pela W3C.

package br.com.ronaldorigoni.FlexAndJava;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
/**
* Taglib para formatacao de texto proveniente do RichTextEditor do AdobeFlex
* @author ronaldorigoni.com.br
*
*/
public class XHTMLFormatting extends SimpleTagSupport {

/**
* Texto a ser convertido
*/
private String textToConverter;
/**
* Execucao da tag
*/
@Override
public void doTag() throws JspException, IOException {
super.doTag();
if(textToConverter!= null)
getJspContext().getOut()
.println(convertFlexTextToXHTML(textToConverter));
}

private String convertFlexTextToXHTML(String text){
String str = text;
// removendo todos os  TEXTFORMAT por espaco em branco
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;TEXTFORMAT.*?&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;);
// removendo todas as aberturas de tags  FONT
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;FONT.*?&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;);
// removendo todos as fechamentos de tags  FONT
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/FONT.*?&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;);
// Alterando atributo HREF para minusculo, padrao HTML
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;A HREF&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;a href&amp;amp;amp;amp;quot;);
// Convertendo todos os links para minusculo
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/A&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/a&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
//  Alterando target para rel externo ( attr manipulado por JS )
str = str.replaceAll(&amp;amp;amp;amp;quot;TARGET=&amp;amp;amp;amp;quot;_blank&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;target=&amp;amp;amp;amp;quot;externo&amp;amp;amp;amp;quot; &amp;amp;amp;amp;quot;);
// convertendo todas as aberturas de tags I para &amp;amp;amp;amp;lt;em&amp;amp;amp;amp;gt;
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;I&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;em&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// convertendo todos os fechamentos de tags I para &amp;amp;amp;amp;lt;em&amp;amp;amp;amp;gt;
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/I&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/em&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// alterando abertura de &amp;amp;amp;amp;lt;B&amp;amp;amp;amp;gt; para &amp;amp;amp;amp;lt;strong&amp;amp;amp;amp;gt;  padrao XHTM
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;B&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;strong&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// alterando fechamento de &amp;amp;amp;amp;lt;B&amp;amp;amp;amp;gt; para &amp;amp;amp;amp;lt;strong&amp;amp;amp;amp;gt;  padrao XHTM
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/B&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/strong&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// alterando para maiuscula para minuscula
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;U&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;u&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/U&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/u&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// formatando para minusculo
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/LI&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;LI&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/li&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;li&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// formatando para minusculo
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/LI&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/li&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;/ul&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// todo o inicio de uma &amp;amp;amp;amp;lt;li&amp;amp;amp;amp;gt;  deve ser procedido por uma &amp;amp;amp;amp;lt;ul&amp;amp;amp;amp;gt; por isso
// quando encontrarmos uma abertura de &amp;amp;amp;amp;lt;LI&amp;amp;amp;amp;gt; devemos
// abrir antes disto uma  &amp;amp;amp;amp;lt;ul&amp;amp;amp;amp;gt;
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;LI&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;ul&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;li&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// removendo formatacao de paragrafo,
// ( formatacao deve ser feita por CSS )
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;P ALIGN=&amp;amp;amp;amp;quot;LEFT&amp;amp;amp;amp;quot;&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;p&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;P ALIGN=&amp;amp;amp;amp;quot;CENTER&amp;amp;amp;amp;quot;&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;p&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;P ALIGN=&amp;amp;amp;amp;quot;RIGHT&amp;amp;amp;amp;quot;&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;p&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;P ALIGN=&amp;amp;amp;amp;quot;JUSTIFY&amp;amp;amp;amp;quot;&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;p&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// todo o fechamento de paragrafo
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/P&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/p&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;);
// removendo todo o final de &amp;amp;amp;amp;lt;/TEXTFORMAT&amp;amp;amp;amp;gt;
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/TEXTFORMAT.*?&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;);
// removendo todas as UL vazias
str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;/ul&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;ul&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;);
/*
Caso queira remover os paragrafos em branco e principalmente
as listas em branco  resultandes de um ENTER no editor do Flex
descomente as duas linhas abaixo.
*/
//str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;p&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;/p&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;);
//str = str.replaceAll(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;lt;li&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;/li&amp;amp;amp;amp;gt;&amp;amp;amp;amp;quot;,&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;);
return str;

}
/**
*  Getter  textToConverter
*/
public String getTextToConverter() {
return textToConverter;
}
/*
*  Setter textToConverter
*/
public void setTextToConverter(String textToConverter) {
this.textToConverter = textToConverter;
}

Este exemplo é a criação de uma Custom TagLibrary, mas pode também ser adaptado para outras linguagens.
Surgindo 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