segunda-feira, 12 de novembro de 2007

Granite DS + Adobe Flex Builder

eis a grande questão... comecei semana passada a estudar o uso do Granite DS que me possibilita o uso do protocolo AMF3 para comunicação entre Client & Server...

* a problemática: legal, peguei os exemplos do Granite DS, os .war neles contidos, coloquei no Tomcat e funcionou... dai me veio a questão... como faço para usar isso em um projeto usando o Flex Builder?

* o que usei? segue o que usei nesse meu teste:
- Java 1.5
- Eclipse 3.2 + WTP 2.0 + Adobe Flex Builder Plugin
- Tomcat 5.5.20
- OC4J 10.1.3.20 (servidor de app da Oracle usado como servidor de teste de deploy do projeto)
- graniteds-pojo-0.4.0 (como base para o projeto server side)

* criando projeto para o server side da aplicação
- criar um projeto no Eclipse Dynamic Web Project
- criei um diretório para o projeto com o nome GranitePojoFlex
- após criado o projeto no diretório de nome definido
- copiar os arquivos do graniteds-pojo-0.4.0 para o projeto
> web.xml, códigos, diretórios do WEB-INF
- criei um diretório no /WebContent para o projeto Client Side - Adobe Flex, com nome de flex
- defini nas propriedades do projeto, no item: Web Project Settings, o Context Root para: granitepojoflex

* criando o projeto client side
- criei o projeto no diretório flex citado anteriormente
- o mxml do graniteds-pojo-0.4.0, copiei o conteúdo desse para o arquivo main.xml
- copiei o arquivo /WEB-INF/flex/services-config.xml, para o raíz do projeto
- editei esse arquivo services-config.xml

de:

    <channels>
        <channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
            <endpoint
                uri="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>


para:

    <channels>
        <channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
            <endpoint
                uri="http://{server.name}:{server.port}/granitepojoflex/graniteamf/amf"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>


- feito isso é hora de adicionar esse arquivo nos parametros de compilação do projeto, indo nas propriedades, em Flex Compiler, onde adicionei o seguinte: -services "services-config.xml", com isto ira dizer onde está nossos dados...
- ainda é necessário configurar um outro detalhe nas propriedades do projeto, no item Flex Build Path, o item: Output folder URL, para dizer onde executar a aplicação em Flex... e como nesse caso estamos utilizando o tomcat o valor para esse item será: http://localhost:8080/granitepojoflex/flex/bin, o como esse projeto do Flex está dentro do /WebConten/Flex do projeto Server Side, justifica o porque dessa URL

* hora de executar
- primeiro colocar o projeto server side para executar, como não tem nenhum JSP, não irá aparecer nada quando o servidor e a aplicação estiver rodando
- agora hora de executar o projeto Flex, só mandar executar nosso main.mxml...
- lembrando que foi configurado a URL, você irá observar que estará executando em um endereço URL no seu browser

e eis um projeto em Flex Builder + Granite

* fazendo um deploy em um servidor para testar
no caso usei o OC4J, mas antes...
- no projeto Server Side, clique com o botão direito e vá em exportar WAR...
- para facilitar exporte para o Desktop por exemplo
- no OC4J, na parte de administração eu selecionei esse .war gerado e realizei o deploy...
- após iniciado a aplicação eu usei a seguinte URL para acessar e testar: http://localhost:8888/granitepojoflex/flex/bin/main.html, a porta é 8888, pois essa é a padrão do OC4J

segue o projeto Zipado: [link]

Obs.: esse projeto usa a versão modificada do Granite DS, falado nesse [post]

Granite DS, mapeamento flexível

hoje estudando o Granite Data Services, que é uma alternativa OpenSource e gratuita ao LiveCycles da Adobe, acabei encontrando uma informação que julgo eu ser muito útil...

estava vendo o funcionamento do AMFPHP, onde verifiquei que o mapeamento das classes são flexíveis, onde posso ter múltiplas classes mapeadas, enquanto no granite, eu tinha que mapear apenas uma única classe de cada vez... mas segundo a sugestão repassada, tem como deixar o Granite funcionando de forma similar ao AMFPHP, que em tese é mais fácil de reutilizar o mapeamento...

a dica está nesse [link flex-brasil]

onde é sugerido a alteração da classe: org.granite.messaging.service.SimpleServiceFactory.java

para que o Granite possa ser utilizado de forma flexível:

classe original:
package org.granite.messaging.service;

import flex.messaging.messages.RemotingMessage;

import org.granite.config.flex.Destination;
import org.granite.context.GraniteContext;

import java.util.Collections;
import java.util.Map;

/**
 @author Franck WOLFF
 */
public class SimpleServiceFactory extends ServiceFactory {

    @Override
    public ServiceInvoker<?> getServiceInstance(RemotingMessage requestthrows ServiceException {
        String messageType = request.getClass().getName();
        String destinationId = request.getDestination();
        
        GraniteContext context = GraniteContext.getCurrentInstance();
        Destination destination = context.getServicesConfig().findDestinationById(messageType, destinationId);
        if (destination == null)
            throw new ServiceException("No matching destination: " + destinationId);
        String scope = (String)destination.getProperties().get("scope");
        
        Map<String, Object> cache = null;
        if (scope == null || "request".equals(scope))
            cache = context.getRequestMap();
        else if ("session".equals(scope))
            cache = context.getSessionMap();
        else if ("application".equals(scope))
            cache = Collections.synchronizedMap(context.getApplicationMap());
        else
            throw new ServiceException("Illegal scope in destination: " + destination);
        
        String key = SimpleServiceInvoker.class.getName() '.' + destination.getId();
        SimpleServiceInvoker service = (SimpleServiceInvoker)cache.get(key);
        if (service == null) {
            service = new SimpleServiceInvoker(destination, this);
            cache.put(key, service);
        }
        return service;
    }
}

classe alterada:
package org.granite.messaging.service;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.granite.config.flex.Destination;
import org.granite.context.GraniteContext;

import flex.messaging.messages.RemotingMessage;

/**
 @author Franck WOLFF
 @author Erko Bridee de Almeida Cabrera
 
 * Alterações da classe para possibilitar um mapeamento
 * flexivel para qualquer classe do projeto
 
 * * dica do link:
 * http://br.groups.yahoo.com/group/flex-brasil/message/7933
 * - Graniteds com unico mapeamento de Classe
 
 */
public class SimpleServiceFactory extends ServiceFactory {

    @Override
    public ServiceInvoker<?> getServiceInstance(RemotingMessage requestthrows ServiceException {
        String messageType = request.getClass().getName();
        String destinationId = request.getDestination();
        
        GraniteContext context = GraniteContext.getCurrentInstance();
        Destination destination = context.getServicesConfig().findDestinationById(messageType, destinationId);
        if (destination == null)
            throw new ServiceException("No matching destination: " + destinationId);
        String scope = (String)destination.getProperties().get("scope");
        
        Map<String, Object> cache = null;
        if (scope == null || "request".equals(scope))
            cache = context.getRequestMap();
        else if ("session".equals(scope))
            cache = context.getSessionMap();
        else if ("application".equals(scope))
            cache = Collections.synchronizedMap(context.getApplicationMap());
        else
            throw new ServiceException("Illegal scope in destination: " + destination);
        
        /*************/
        Destination d = destination;
        ifdestination.getProperties().get("source").equals("*") ){
          Map<String, Object> propertiesMap = new HashMap<String, Object>();
          propertiesMap.put("source", request.getSource());
          d = new Destination(destination.getId(),
          destination.getChannelRefs
          (), propertiesMap, destination.getRoles());
        }
        /**************/
        
        /* String key = SimpleServiceInvoker.class.getName() + '.' + destination.getId();
        SimpleServiceInvoker service = (SimpleServiceInvoker)cache.get(key);
        if (service == null) {
            service = new SimpleServiceInvoker(destination, this);
            cache.put(key, service);
        } ALTERADO */
        
        String key = SimpleServiceInvoker.class.getName() '.' +
        d.getProperties().get("source")//<--Muda Aqui
        SimpleServiceInvoker service = (SimpleServiceInvoker)cache.get(key);
        ifservice == null ) {
          service = new SimpleServiceInvokerd, this )//<--Muda Aqui
          cache.put(key, service);
        }
        
        return service;
    }
}

com isto no projeto do código fonte do GraniteDS, basta usar o build.xml para regerar o .jar do Granite e utilizar este novo jar gerado...

com isto no exemplo do Granite-Pojo, o services-config.xml fica de outra forma:

    <services>
        <service
            id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="pojo">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <scope>session</scope>
                    <source>*</source>
                </properties>
            </destination>
        </service>
    </services>



e no código mxml, no mx:RemoteObject, você deverá indicar o atributo source, como mostrado abaixo:

    <mx:RemoteObject
        id="srv"
        showBusyCursor="true"
        destination="pojo"
        source="test.pojo.PojoService"/>



feito isto, com um único mapeamento, você poderá acessar n-classes do seu projeto...
melhor, o que achou?

Traduzindo componentes do Adobe Flex

algo muito útil para o pessoal que anda utilizando o Adobe Flex, naturalmente que os componentes do SDK do Flex estão em inglês, mas não tem como traduzir eles não?

bom tem sim e foi isso que o Fábio Vedovelli mostrou em um screencast muito interessante, onde pode ser visto no respectivo [link]

Fonte : Fábio Vedovelli