Vamos configurar um projeto simples, que aborde os principais recursos do GraniteDS usando Spring/Hibernate.
Vamos utilizar o projeto do Willian Draï, ele está disponível em http://github.com/wdrai/wineshop-admin é necessário ter o Maven 3.x.
Para baixar o projeto e executar de forma rapida, basta seguir os passos a baixo.
git clone git://github.com/wdrai/wineshop-admin.git cd wineshop-admin mvn clean package cd webapp mvn jetty:run-war
Agora basta acessar o seguinte endereço http://localhost:8080/wineshop-admin/wineshop-admin.swf, os usuários disponíveis são admin/admin e user/user.
Este exemplo é um crud simples que permite criar, editar e pesquisar. O Layout da app é feia, mas seu objetivo é simplesmente demonstrar as seguintes características:
Cada tópico é correspondente a uma tag no GitHub, isso vai servir para que você saiba exatamente o que foi alterado em cada tópico.
Vamos reconstruir o projeto do zero.
Esta é a parte mais facil
mvn archetype:generate -DarchetypeGroupId=org.graniteds.archetypes -DarchetypeArtifactId=org.graniteds-tide-spring-jpa -DarchetypeVersion=1.1.0.GA -DgroupId=com.wineshop -DartifactId=wineshop-admin -Dversion=1.0-SNAPSHOT
Em seguida verifique se o projeto está funcional
cd wineshop-admin mvn clean package cd webapp mvn jetty:run-war
Acesse o seguinte endereço http://localhost:8080/wineshop-admin/wineshop-admin.swf. Você deve ser capaz de ver o aplicativo Hello World.
Este passo será maior, nele iremos construir a maior parte da aplicação. Entidade JPA, Serviço do Spring e um Cliente Flex básico.
Está é nossa entidade, não há nada de especial.
@Entity
public class Vineyard extends AbstractEntity {
private static final long serialVersionUID = 1L;
@Basic
private String name;
@OneToMany(cascade=CascadeType.ALL, mappedBy="vineyard",
orphanRemoval=true)
private Set wines;
//Get's e Set's omitido
}
@Entity
public class Wine extends AbstractEntity {
private static final long serialVersionUID = 1L;
public static enum Type {
RED,
WHITE,
ROSE
}
@ManyToOne
private Vineyard vineyard;
@Basic
private String name;
@Basic
private Integer year;
@Enumerated(EnumType.STRING)
private Type type;
//Get's e Set's omitidos
}
Interface do serviço Spring para trabalhar com este modelo.
@RemoteDestination
@DataEnabled(topic="")
public interface WineshopService {
public void save(Vineyard vineyard);
public void remove(Long vineyardId);
public Map list(Vineyard filter,
int first, int max, String[] sort, boolean[] asc);
}
Como você pode observar temos duas Anotações @RemoteDestination indica que o serviço deve ser exposto ao Flex e um proxy ActionScript3 será gerado para o serviço. @DataEnable indica que o GraniteDS irá acompanhar as atualizações das entidades JPA e envia-las automaticamente para os clientes.
Implementação do Serviço
@Service
public class WineshopServiceImpl implements WineshopService {
@PersistenceContext
private EntityManager entityManager;
@Transactional
public void save(Vineyard vineyard) {
entityManager.merge(vineyard);
entityManager.flush();
}
@Transactional
public void remove(Long vineyardId) {
Vineyard vineyard = entityManager.find(Vineyard.class, vineyardId);
entityManager.remove(vineyard);
entityManager.flush();
}
@Transactional(readOnly=true)
public Map list(Vineyard filter,
int first, int max, String[] sort, boolean[] asc) {
StringBuilder sb = new StringBuilder("from Vineyard vy ");
if (filter.getName() != null)
sb.append("where vy.name like '%' || :name || '%'");
if (sort.length > 0)
sb.append("order by ");
for (int i = 0; i < sort.length; i++)
sb.append(sort[i]).append(" ").append(asc[i] ? " asc" : " desc");
Query qcount = entityManager.createQuery("select count(vy) "
+ sb.toString());
Query qlist = entityManager.createQuery("select vy "
+ sb.toString()).setFirstResult(first).setMaxResults(max);
if (filter.getName() != null) {
qcount.setParameter("name", filter.getName());
qlist.setParameter("name", filter.getName());
}
Map result = new HashMap(4);
result.put("resultCount", (Long)qcount.getSingleResult());
result.put("resultList", qlist.getResultList());
result.put("firstResult", first);
result.put("maxResults", max);
return result;
}
}
Este é um serviço classico do Spring com JPA. Porém temos algumas particularidades.
Mas isto não traz nenhuma dependência com GraniteDS, sendo assim este serviço pode ser usado por qualquer cliente.
Vamos ao cliente Flex:
<?xml version="1.0" encoding="utf-8"?>
<s:VGroup
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:e="com.wineshop.entities.*"
xmlns="*"
width="100%" height="100%">
<fx:Metadata>[Name]</fx:Metadata>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import org.granite.tide.spring.Spring;
import org.granite.tide.collections.PagedQuery;
import org.granite.tide.events.TideResultEvent;
import org.granite.tide.events.TideFaultEvent;
import com.wineshop.entities.Vineyard;
import com.wineshop.entities.Wine;
import com.wineshop.entities.Wine$Type;
import com.wineshop.services.WineshopService;
Spring.getInstance().addComponentWithFactory("vineyards", PagedQuery,
{ filterClass: Vineyard, elementClass: Vineyard, remoteComponentClass: WineshopService, methodName: "list", maxResults: 12 }
);
[In] [Bindable]
public var vineyards:PagedQuery;
[Inject]
public var wineshopService:WineshopService;
private function save():void {
wineshopService.save(vineyard);
}
private function remove():void {
wineshopService.remove(vineyard.id, function(event:TideResultEvent):void {
selectVineyard(null);
});
}
private function selectVineyard(vineyard:Vineyard):void {
this.vineyard = vineyard;
vineyardsList.selectedItem = vineyard;
}
]]>
</fx:Script>
<fx:Declarations>
<e:Vineyard id="vineyard"/>
</fx:Declarations>
<s:VGroup paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10" width="800">
<s:HGroup id="filter">
<s:TextInput id="filterName" text="@{vineyards.filter.name}"/>
<s:Button id="search" label="Search" click="vineyards.refresh()"/>
</s:HGroup>
<s:List id="vineyardsList" labelField="name" width="100%" height="200"
change="selectVineyard(vineyardsList.selectedItem)">
<s:dataProvider><s:AsyncListView list="{vineyards}"/></s:dataProvider>
</s:List>
<s:Button id="newVineyard" label="New" click="selectVineyard(new Vineyard())"/>
</s:VGroup>
<s:VGroup paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10" width="800">
<mx:Form id="formVineyard">
<mx:FormHeading label="{isNaN(vineyard.id) ? 'Create vineyard' : 'Edit vineyard'}"/>
<mx:FormItem label="Name">
<s:Label text="{vineyard.id}"/>
<s:TextInput id="formName" text="@{vineyard.name}"/>
</mx:FormItem>
<mx:FormItem>
<s:HGroup>
<s:Button id="saveVineyard" label="Save"
click="save()"/>
<s:Button id="removeVineyard" label="Remove"
enabled="{!isNaN(vineyard.id)}" click="remove()"/>
</s:HGroup>
</mx:FormItem>
</mx:Form>
</s:VGroup>
</s:VGroup>
Está não é uma parte complicada, mas temos alguns pontos que devem ser observados.
Agora você pode fazer um novo build com mvn package, reniciar o jetty e ver as mudanças.
Aqui não há muito a fazer, basta adicionar um itemrender com um componente de Formulário para permitir a edição dos vinhos da vinha selecionada.
<s:FormItem label="Wines">
<s:HGroup gap="10">
<s:List id="formWines" dataProvider="{vineyard.wines}">
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer>
<s:states><s:State name="normal"/></s:states>
<s:HGroup id="wineEdit">
<s:TextInput text="@{data.name}"/>
<s:TextInput text="@{data.year}"/>
<s:DropDownList
selectedItem="@{data.type}"
requireSelection="true"
dataProvider="{outerDocument.wineTypes}"
labelField="name"/>
</s:HGroup>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
<s:VGroup gap="10">
<s:Button label="+"
click="vineyard.wines.addItem(new Wine(vineyard))"/>
<s:Button label="-"
enabled="{Boolean(formWines.selectedItem)}"
click="vineyard.wines.removeItemAt(formWines.selectedIndex)"/>
</s:VGroup>
</s:HGroup>
</s:FormItem>
Deixamos passar duas coisas: Precisamos mudar o construtor da classe Wine para aceitar como argumento a classe Vineyard. Isto será utilizado pela adição de um novo “Wine”
public function Wine(vineyard:Vineyard = null):void {
this.vineyard = vineyard;
}
E inicializar a coleção de Wines para a nova Vineyard:
public function Vineyard():void {
this.wines = new ArrayCollection();
}
Recrie a aplicação com mvn package e reinicie o jetty.
Como você pode observar isto é puramente código Flex. Deixamos para o “Cascade” salvar nossas alterações no banco de dados.
Mas quando as entidades Vineyards são buscadas as listas de Wines ainda não são carregadas. Quando o usuário seleciona uma Vineyard automaticamente ele irá carrega a lista de wines. Isto é completamente transparente para que você não precise pensar nisso.
Se você ja rodou a aplicação pode ter notado que o uso de bindings bidirecionais leva a um comportamento estranho. Mesmo sem salvar as alterações, os objetos locais são modificados. GrantieDS é capaz de rastrear as todas as modificações feitas nas entidades Gerenciadas e é capaz de restaurar o estado estável dos objetos (normalmente a ultima busca feita no servidor).
Tambem é possivel de forma facil habilitar ou desabilitar o botão “Save”, dependendo do estado do objeto.
Para conseguir isso, precisamos apenas de algumas linhas, quando o usuário seleciona outro elemento na lista principal para restaurar o elemento anterior:
import org.granite.tide.spring.Context;
[Inject] [Bindable]
public var tideContext:Context;
private function selectVineyard(vineyard:Vineyard):void {
Managed.resetEntity(this.vineyard);
tideContext.vineyard = this.vineyard = vineyard;
vineyardsList.selectedItem = vineyard;
}
Então podemos usar a propriedade meta_dirty do contexto Tide para habilitar/desabilitar o botão “Save”
<s:Button id="saveVineyard" label="Save"
enabled="{tideContext.meta_dirty}" click="save()"/>
mvn clean package, jetty, …
Agora ja podemos criar, editar e pesquisar no nosso banco de dados. Agora gostariamos de assegurar que os dados são consistentes. Em vez de definirmos validadores Flex para cada campo, vamos usar a api Bean Validation no servidor e a implementação de GraniteDS no cliente.
Primeiro vamos adicionar algumas anotações de Bean Validation ao nosso modeo.
@Basic
@Size(min=5, max=100,
message="The name must be between {min} and {max} characters")
private String name;
@Basic
@Min(value=1900,
message="The year must be greater than {value}")
@Max(value=2050,
message="The year must be less than {value}")
private Integer year;
@Enumerated(EnumType.STRING)
@NotNull
private Type type;
@Basic
@Size(min=5, max=100,
message="The name must be between {min} and {max} characters")
private String name;
@OneToMany(cascade=CascadeType.ALL,
mappedBy="vineyard", orphanRemoval=true)
@Valid
private Set wines;
Isto vai garantir que não podemos salvar entidades inválidas. No entanto gostariamos de informar ao usuário que a operação falhou. Uma maneira feia de fazer isso seria usar um fault Handler com um Alert. Em vez disso vamos usar o componente FormValidator que irá validar a entidade localmente e interpretar as exceçoes do servidor e propagar as mensagens para o campo correto.
Primeiro precisa registrar o ValidatorExceptionHandler que irá processar os erros de validação provenientes do servidor. Neste exemplo isto não é necessário, pois todas as restrições podem ser processadas no cliente. Mas é util, caso o serviço tenha restrições adicionais. Basta adicionar está linha no método init do main.mxml.
Spring.getInstance().addExceptionHandler(ValidatorExceptionHandler);
Em Home.xml defina o namespace “v” e defina um FormValidator ligado ao formulário de edição e a entidade.
<s:VGroup
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:v="org.granite.validation.*"
xmlns:e="com.wineshop.entities.*"
xmlns="*"
width="100%" height="100%"
initialize="selectVineyard(new Vineyard())">
<fx:Declarations>
<e:Vineyard id="vineyard"/>
<s:ArrayCollection id="wineTypes" source="{Wine$Type.constants}"/>
<v:FormValidator id="formValidator"
entity="{vineyard}"
form="{formVineyard}"/>
</fx:Declarations>
Também podemos definir um FormValidator para o item render:
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer>
<fx:Declarations>
<v:FormValidator id="wineValidator"
form="{wineEdit}" entity="{data}"/>
</fx:Declarations>
<s:states><s:State name="normal"/></s:states>
<s:HGroup id="wineEdit">
<s:TextInput text="@{data.name}"/>
<s:TextInput text="@{data.year}"/>
<s:DropDownList
selectedItem="@{data.type}"
requireSelection="true"
dataProvider="{outerDocument.wineTypes}"
labelField="name"/>
</s:HGroup>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
Estás duas declarações permitirá exibir mensagens de erro no campo durante a edição. O FormValidator utiliza as anotações de validação do bean ActionScript3 para saber quais validações precisam ser aplicadas.
E por fim podemos impedir o usuário de salvar adicionando uma linha.
private function save():void {
if (formValidator.validateEntity())
wineshopService.save(vineyard);
}
mvn package, jetty, …
Habilitar o Data Push é apenas uma questão de configuração. Temos 4 itens a verificar.
<graniteds:messaging-destination id="wineshopTopic" no-local="true" session-selector="true"/>
@RemoteDestination
@DataEnabled(topic="wineshopTopic", publish=PublishMode.ON_SUCCESS)
public interface WineshopService {
Spring.getInstance().addComponent("wineshopTopic", DataObserver);
Spring.getInstance().addEventObserver("org.granite.tide.login",
"wineshopTopic", "subscribe");
Spring.getInstance().addEventObserver("org.granite.tide.logout",
"wineshopTopic", "unsubscribe");
Claro que as três declarações devem usar o mesmo nome do tópico. Mas isto é tudo o que você precisa para permitir o envio de dados.
mvn package, jetty, …
Agora basta abrir vários browsers, e todas as alterações feitas em um navegador devem ser enviadas a todos os outros.
Em qualquer aplicação multiusuário, pode haver vários usuários fazendo alterações simultaneamente na mesma entidade. Usar optimistic look é a forma commun de lidar com estes casos e evitar inconssistência dos dados. O GraniteDS é capaz de lidar com este problema tanto em chamadas normais quanto com chamadas em real-time data push.
Isso é muito simples de configurar, você só precisar registrar um manipuladaro para exceções JPA OptimistickLockException e um event listener no contexto do Tide, que será chamado quando um conflito por modificação concorrente acontecer.
private function init():void {
...
Spring.getInstance().addExceptionHandler(OptimisticLockExceptionHandler);
Spring.getInstance().getSpringContext().addEventListener(
TideDataConflictsEvent.DATA_CONFLICTS, conflictsHandler);
}
private function conflictsHandler(event:TideDataConflictsEvent):void {
Alert.show("Someone has modified this vineyard at the same time\n. "
+ "Keep your changes ?",
"Conflict", Alert.YES | Alert.NO, null, function(ce:CloseEvent):void {
if (ce.detail == Alert.YES)
event.conflicts.acceptAllClient();
else
event.conflicts.acceptAllServer();
});
}
A parte mais difícil realmente é obter um conflito. Após mvn package, jetty… Abra dois navegadores, crie uma vinha no primeiro, ele irá aparecer no segundo. Edite o registro no segundo navegador altere o nome mas não salve. No primeiro navegador mude para um nome diferente e salve. No segundo navegador um alerta deve aparecer.
Este ultimo passo não é visual mas pode melhorar muito o desempenho da sua aplicação. O Suporte a lazy loading servidor-cliente garante que a quantidade de dados transferidos neste sentido é limitado, mas pode ocorrer um problema no sentido contrário(cliente-servidor). Uma vez que todo o grafo do seu Objeto é carregado para o cliente, o grafo todo será enviado ao servidor, mesmo que você tenha alterado sómente uma propriedade raiz do seu objeto. Com grafos mais profundos e complexos isto pode prejudicar o desenpenho de operações de salvar.
Para resolver isso GraniteDS oferece agora um novo recurso chamado de “reverse lazy loading”.
Isto pode ser configurado da seguinte forma
Spring.getInstance().addComponents([UninitializeArgumentPreprocessor]);
E então basta adicionar no método de update a anotação @Lazy para os argumentos de entrada
public void save(@Lazy Vineyard vineyard);
Agora você é capaz de ver o que o GraniteDS pode oferecer e como ele pode simplificador o seu desenvolvimento, e até mesmo trazer novas possibilidades para sua aplicação
Agora você tem duas maneiras rapidas de inciar um novo projeto com GraniteDS: Um asistente para o Eclipse e os quatro Archetypes Maven. As duas formas permitem criar projetos totalmente funcionas com apenas alguns cliques ou com poucas linhas de comando, resultando em um projeto pré-configurado, pronto para implamtar. Das duas formas você terá o “esqueleto” de uma aplicação com os principais recursos, que você utilizaria em uma aplicação real.
Se você for um usuário Maven, pode começar com os archetypes que trabalham com servidores incorporados (Jetty, GlassFish) veja este paragrafo abaixo. Usando o assistente pode ser útil na configurações de outros servidores, tais como Tomcat e JBoss.
Primeiro você precisa adicionar o plugin Assistente do GraniteDS ao Eclipse. Para fazer isso, voccê pode procurar por GraniteDS no Eclipse Marketplace ou Adicionar o endereço http://www.graniteds.org/public/update-site/ a sua lista de sites disponíveis.
Você precisa instalar os dois plugins GraniteDS Gas 3 Builder e GraniteDS New Project Wizard, ele vai registrar nos wizards do eclipse em GraniteDS/GraniteDS Project três templates padrão.
Os três modelos para criar projetos Flex/Java são facilmente convertidos para um projeto Eclipse WTP, e fazer deploy local ou remoto em um servidor de aplicações. Se você usar Flash Builder os arquivos de configuração necessárias podem ser gerados opcionalmente, e finalmente um arquivo ant é gerado para que você possa criar o build de forma manual, se necessário.
Selecion o menu File/New/Project, procure por GraniteDS e selecione GraniteDS Project
Selecione um modelo e clique em Next.
Escolha um nome para o Projeto, escolha suas tecnologias preferidas, por exemplo: Spring 3, Tomcat 7 e Hiberante. Preencher as outras informações, em particular o Flex SDK deve ser seu diretório home(de preferência deve ser a versão 4.5), e a pasta de deploy do servidor de aplicações (por exemplo: /home/dev/apache-tomcat-7.0.22/webapps para Tomcat 7). Mantenha as outras opções, verifique se “Configure Flash Builder” e “Create Ant+Ivy build file” estão selecionados, clique em Finish.
A criação do novo projeto pode levar alguns minutos, porque o assistente vai buscar todas as bibliotecas necessárias nos repositórios do Maven. Se você estiver usando Flash Builder 4.5, você pode receber o seguinte aviso, isso porque os arquivos de configuração gerados são direcionados para o Flash Builder 4, então basta selecionar Flex SDK 4.5 +
Com o Flash Builder, você sempre terá um erro sobre os arquivos “HTML wrapper” depois que o projeto é criado. Basta clicar sobre a mensagem de erro, conforme sugerido e selecione “Recriar templates HTML” .
Se você não usar o Flash Builder, você pode simplesmente usar o Ant com o arquivo build.xml que foi gerado, que irá executar a compilação da aplicação Flex.
Neste ponto, você já tem um projeto totalmente configurado para as tecnologias e servidor que você escolheu na página do assistente. Você tem agora duas opções para implantá-lo em seu servidor: Fazer o deploy do war gerado pela compilação feita pelo Ant ou usar o Eclipse WTP.
Para usar WTP, você primeiro tem que converter o projeto. Com o botão direito sobre o projeto e selecionando o menu Configure/Convert to Form Faceted ...
Na próxima tela, selecione Dynamic Web Project (3.0 para Tomcat 7 ou JBoss 6/7, 2.5 para Tomcat 6 ou JBoss 4/5) e selecione o servidor correspondente.
Finalmente, clique sobre o projeto e selecione Debug As/Debug on server.
Na última tela, basta verificar que o servidor correto está selecionado, clique em Finish .
Eclipse irá iniciar o servidor de aplicação e abrir um browser na página de aplicação.Você deve obter algo como isto:
Você pode logar com admin:admin ou usuário:usuário, e adicionar alguns nomes. Se você abrir um segundo navegador (e não apenas aba ou outra janela do mesmo navegador! ) e apontá-lo para a mesma página ( http://localhost:8080/springgds ), você deve ver as suas modificações refletidas em tempo real, em ambos os navegadores.
Se você configurar a publicação automática no Eclipse WTP (que deve ser o caso por padrão), qualquer mudança que você fizer na aplicação Flex será automaticamente implantada no servidor. Você pode simplesmente atualizar a página para verificar as suas alterações, uma vez compilado, não há necessidade de reimplantar nada para desenvolver a sua UI!
Se você é um usuário Maven, é provável que você prefira iniciar com um archetype. Há quatro archetypes diferentes para o GraniteDS, eles estão disponíveis no repositório central do Maven:
archetypeGroupId: org.graniteds.archetypes archetypeVersion: 1.1.0.GA archetypeArtifactId:
Os archetypes com Tide são equivalentes a criar um projeto Spring/EJB/Seam/CDI template com o assistente do Eclipse. As principais diferenças são que você não precisa ter um SDK do flex instalado, que será baixado dos repositórios do maven. E você tem três projetos distintos, um projeto Java, um projeto Flex e um projeto WebApp.
Vamos reproduzir o que fizemos com o Assistente de Eclipse, primeiro com uma linha de comando (Maven 3.x obrigatório):
mvn archetype:generate -DarchetypeGroupId=org.graniteds.archetypes -DarchetypeArtifactId=graniteds-tide-spring-jpa-hibernate -DarchetypeVersion=1.1.0.GA -DgroupId=org.example -DartifactId=springgds -Dversion=1.0-SNAPSHOT
Com o archetype criado você pode compilar o projeto com
cd springgds mvn clean package
E finalmente executar o servidor jetty embutido
mvn jetty:run-war
Agora você pode navegar http://localhost:8080/springgds e verificar se o aplicativo funciona.
Com a integração do Maven com Eclipse (plugin M2E), você pode simplesmente escolher um dos archetypes ao fazer New Project Maven.
Para implantar o aplicativo em um servidor de produção, você pode usar o seguinte comando deo maven para criar um arquivo war:
mvn war:war
No entanto que ao fazer isso, você pode ter que mudar a configuração do aplicativo. Em geral, você tem que mudar o nome do servlet do Gravity no web.xml e muito provavelmente atualizar suas configurações de JPA.
Isto agora leva literalmente 5 minutos (e menos de 1 minuto após a primeira execução) para iniciar um projeto Flex/Java com o novo GraniteDS. Você já não tem desculpas para não experimentar!
Após o lançamento do GraniteDS 2.3.0.GA, foram atualizados os archetypes do Maven, agora estão na versão 1.1.0.GA.
As mudanças são as seguintes.
Para relembrar, segue exemplo de como usar um archetype para criar um projeto simples usando Flex/GraniteDS/Spring e Hibernate.
mvn archetype:generate -DarchetypeGroupId=org.graniteds.archetypes -DarchetypeArtifactId=graniteds-tide-spring-jpa-hibernate -DarchetypeVersion=1.1.0.GA -DgroupId=com.myapp -DartifactId=example -Dversion=1.0-SNAPSHOT
Temos 4 archetypes diferentes. Eles são baseados no Flex 4.5 e nos componentes Spark.
Após criar o projeto você pode compilar ele. Isso é feito de forma facil com:
mvn install
E executar ele no jetty (Spring/Seam) com:
cd webapp mvn jetty:run-war
Ou no GlassFish 3.1.1 embutido (CDI) com:
cd webapp mvn embedded-glassfish:run
Após iniciar você pode acessar o aplicativo no seguinte endereço http://localhost:8080/example/example.swf. Por padrão existem dois usuários que podem acessar a aplicação admin/admin e user/user
Você tembém pode gerar um war com:
mvn war:war
Neste caso você poderá ter que mudar as configurações de servidor caso precise rodar em outros servidores de aplicação. Por exemplo, se for rodar no Apache Tomact será preciso alterar as configurações do Servelet do Gravity no web.xml. Observe também que por padrão é utilizado um banco de dados H2.
Jboss mais uma vez mudou a implementação interna sobre VFS, quebrando o scanner de classes do GraniteDS. Mesmo assim era possível executar o GraniteDS em modo no-scan. Mas isto foi corrigido e ja é possivel utilizar no Jboss AS7.
Outro problema com Jboss AS 7 é a profunda integração que ele tem com Hibernate 4, o que deixa um tanto doloroso implantar aplicações com Hibernate 3.x. Existem algumas soluções descritas no blog do Hibernate. No entanto, é recomendado a atualização para Hibernate 4 logo que possível. GraniteDS já suporta Hibernate 4. Basta usar o grantie-hibernate4.jar ao invés de granite-hibernate.jar
Flex 4.5 quebrou algumas coisas da APIs e havia dois problemas principais com Tide:
Estes dois itens foram corrigidos e você irá encontrar um versão das bibliotecas do GraniteDS compiladas com Flex4.5 aqui. Infelizmente, ainda não está disponível com a distribuição “normal”, porque o sistema de compilação não foi capaz de compilar com versões diferentes do Flex SDK.
O suporte a lazy-loading do GraniteDS é uma das suas características mais importantes e ajuda muito na quantidade de dados transferidos através da rede. No entanto só funcionava do Servidor para o Cliente. O problema é que depois de ter carregado todas as associações no cliente, passando um objeto para uma chamada ao servidor, o método irá enviar todo o grafo do objeto para o servidor, mesmo se você tiver mudado uma propriedade simples.
public function savePerson():void {
person.lastName = "Test";
personService.save(person); // This will send all loaded collections associated to the Person object
}
Obviamente isso não é muito eficiente, então agora é possível pedir para o Tide remover a inicialização do grafo do objeto antes de enviá-lo ao servidor. Você pode fazer isso manualmente.
var uperson:Person = new EntityGraphUnintializer(tideContext).uninitializeEntityGraph(person) as Person; personService.save(uperson);
Toda a inicialização de coleção carregada no Objeto Person é removida. O objeto uperson contém o mínimo, de dados, para um correto update no servidor. Se não houver uma mudança profunda no grafo do objeto o uninitializer é capaz de identificar e enviar os dados para o servidor.
person.contacts.getItemAt(0).email = 'test@test.com'; var uperson:Person = new EntityGraphUnintializer(tideContext).uninitializeEntityGraph(person) as Person; personService.save(uperson)
Aqui o objeto uperson ainda vai conter a coleção de contacts, mas se houver outras coleções elas serão removidas.
Ficar chamando o EntityGraphUninitializer manualmente é trabalhoso e se torna feio. Para uma solução mais limpa você pode anotar os parametros dos seus métodos de serviço com a anotação @org.granite.tide.data.Lazy.
public void save(@Lazy Person person) {
}
Desta forma o GAS3 irá gerar uma anotação [Lazy] nos seus métodos de serviço. Em seguida deve registrar o componente UninitializeArgumentPreprocessor no Tide.
Tide.getInstance().addComponentes([UninitializeArgumentPreprocessor]);
Depois de feito isso todas as chamadas para PersonService.save() irá utilizar o objeto não inicializado do argumento person.
No GraniteDS 2.2 é necessário um ServletContext para recuperar o Singleton do Gravity, o que nem sempre é possível ou adequado e implica na dependência da API de Servlets. Com GrantieDS 2.3 o Singleton está disponivel no contexto do framework e pode ser simplesmente injetado.
Com Spring ou CDI
@Inject private Gravity gravity;
Com Seam
@In("org.granite.seam.gravity") private Gravity gravity;
As capacidades de DI do EJB3 são limitadas para permitir algo assim. Se você precisa ser indepente da API do Gravity, basta usar um Tópico JMS e enviar mensagens com a API de JMS.
GraniteDS fornece um recurso de envio de mensagens que permite acompanhar as atualizações de entidades JPA e de forma trasparante despachá-las através do Gravity para os clientes Flex. No entanto no GrantieDS 2.2 havia duas limitações: as atualizações podem ser controladas apenas de um segmento gerenciado pelo GraniteDS e o modo ON_COMMIT permitindo envio transacional das atualizações não era suportado.
GraniteDS 2.3 vem com um conjunto de interceptadores(infelizmente um para cada tecnologia) para gerenciar o modo ON_COMMIT que pode ser usado para rastrear qualquer mudança fora do segmento do GraniteDS.
A configuração é simples, basta adicionar o parametro useInterceptor=true na anotação @DataEnabled e usar ON_SUCCESS ou ON_COMMIT.ON_SUCCESS é o modo padrão e significa simplesmente que o envio ocorrerá para todas as chamadas recebidas. ON_COMMIT significa que o envio ocorrerá quando a transação for confirmada, ele garante que o envio é transacional, quando utilizado em conjunto com um tópico JMS transacionados.
Em seguida, configure o interceptador para a sua estrutura de destino:
Para Spring adicionar isto ao contexto
<graniteds:tide-data-publishing-advice/>
Tome cuidado que você precisa utilizar a ultima versão do xsd
xsi:schemaLocation="http://www.graniteds.org/config http://www.graniteds.org/public/dtd/2.3.0/granite-config-2.3.xsd"/
Para Seam não precisa nada de configuração quando o parametro useInterceptor=true é adicionado a anotação @DataEnabled.
Para CDI apenas habilitar o interceptor no beans.xml
<beans
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>org.granite.tide.cdi.TideDataPublishingInterceptor</class>
</interceptors>
</beans>
Para EJB3, você tem que configurar o interceptor em cada EJB:
@DataEnabled(topic="myTopic", publish=PublishMode.ON_COMMIT, useInterceptor=true) public class MyServiceBean { ... }
Ou globalmente no ejb-jar.xml:
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>org.granite.tide.ejb.TideDataPublishingInterceptor</interceptor-class>
</interceptor-binding>
...
</assembly-descriptor>
Olá Pessoal.
Quarta feira dia 09/11 foi lançada a versão 2.3.0 GA(Final) do GraniteDS. Está versão contem com várias correções de bugs encontrados após o lançamento da versão 2.3.0 RC1. Para acompanhar as correções feitas segue veja aqui, este link leva direto as correções feitas entre as versões 2.3.0 RC1 e 2.3.0 GA
As novidades mais interessantes são as seguintes.
Pra quem utiliza maven está versão ja está disponível nos repositorios.
Olá pessoal vamos a mais um post. Hoje iremos falar sobre GAS3 e a integração com FlexMojos. Para quem não conhece o FlexMojos foi criado por um Brasileiro o Marvin Froeder(@velobr).
Vou partir do principio que todos ja conhecem o basico de Maven. Para quem ainda não conhece e tem interesse na minha opnião o melhor materia de maven é este aqui.
Abaixo segue um exemplo de um pom.xml com um plugin do FlexMojos que gera nossas classes AS3
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.graniteds</groupId>
<artifactId>graniteds-sample</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>swc</packaging>
<name>GraniteDS</name>
<build>
<sourceDirectory>src/main/flex</sourceDirectory>
<plugins>
<plugin>
<groupId>org.sonatype.flexmojos</groupId>
<artifactId>flexmojos-maven-plugin</artifactId>
<version>4.0-RC2</version>
<extensions>true</extensions>
<dependencies>
<dependency>
<groupId>com.adobe.flex</groupId>
<artifactId>compiler</artifactId>
<version>4.5.1.21328</version>
<type>pom</type>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<generatorToUse>graniteds22</generatorToUse>
<baseOutputDirectory>${project.build.sourceDirectory}</baseOutputDirectory>
<outputDirectory>${project.build.sourceDirectory}</outputDirectory>
<extraOptions>
<tide>true</tide>
<entityFactory>org.granite.generator.as3.DefaultEntityFactory</entityFactory>
<outputEnumToBaseOutputDirectory>true</outputEnumToBaseOutputDirectory>
</extraOptions>
<includeJavaClasses>
<include>org.graniteds.entities.*</include>
</includeJavaClasses>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
Como podem ver no exemplo acima As configurações são muito parecidas com as configurações utilizadas com ANT. Todas as configurações tem documentação no próprio site do FlexMojos.
Uma das configurações que mais diferem do ANT, é a tag <generatorToUse> está tag informa qual a versão do GAS3 queremos usar, com ANT se quisermos trocar a versão do GAS3 precisamos baixar a versão que desejamos e subistituir o jar. Com FlexMojos isso é muito mais facil, basta indicar nesta tag que ele ira se encarregar de baixar a versão correta.
As Tags <baseOutputDirectory> e <outputDirectory> indicam aonde as classes serão geradas, no exemplo acima definimos para que o As3.as e As3Base.as sejam gerados no diretório src/ da aplicação.
Em <extraOption> podemos definir outros parametros do GAS3 assim como definimos no plugin para FlashBuilder. Neste exemplo eu adicionei a opção <tide>true</tide> isto indica ao GAS3 que o template utilizado para gerar as classes, são os com suporte ao framework TIDE que veremos nos próximos posts.
Em <includeJavaClasses> podemos indicar em quais pacotes estão nossas entities ou classes que queremos gerar. Todas as classes AS3 são geradas na mesma estrutura que estão as classes java.
Boa Tarde Pessoal.
Agora a pouco Franck Wolff, criador do GraniteDS nos deu está excelente notícia, na lista do GraniteDS no Google Grupos. Foi lançada uma versão Release Candidate do GraniteDS, com novas funcionalidades e várias correções de bugs. Vou descrever um pouco o que foi feito nesta versão.
Foram fixados 59 bugs, foram feitas varias melhorias e adicionadas novas funcionalidades. Para acompanhar todas estas modificações é disponibilizado o Jira. Vou postar aqui o link direto para as modificações referente a está versão
E algumas coisas que merecem destaque nesta versão.
Olá pessoal vamos a mais um post sobre o GAS3.
Hoje vamos falar sobre a integração com Apache ANT. O Ant é uma ferramenta de build que é muito utilizada na comunidade java. Muitos utilizam o ANT em servidores de integração continua, e até mesmo para builds locais para facilitar a compilação/deploy das aplicações. Não vou entrar em detalhes sobre Apache ANT mesmo porque o foco do blog é o GraniteDS.
Primeiro passo é adicionar a linha abaixo no seu build.xml. Esta linha adiciona uma definição de tarefa ao projeto.
<taskdef name="gas3" classname="org.granite.generator.ant.AntJavaAs3Task"/>
E criar um target como o que segue a baixo.
<target name="generate.as3">
<gas3
outputdir="as3"
baseoutputdir="base_as3"
uid="myUidFieldName"
entitytemplate="/myEntityTemplate.gsp"
entitybasetemplate="/myEntityBaseTemplate.gsp"
interfacetemplate="/myInterfaceTemplate.gsp"
beantemplate="/myBeanTemplate.gsp"
beanbasetemplate="/myBeanBaseTemplate.gsp"
enumtemplate="/myEnumTemplate.gsp"
remotetemplate="/myRemoteTemplate.gsp"
remotebasetemplate="/myRemoteBaseTemplate.gsp"
tide="true"
as3typefactory="path.to.MyAs3TypeFactory"
entityfactory="path.to.MyEntityFactory"
remotedestinationfactory="path.to.MyRDFactory"
transformer="path.to.MyTransformer"
externalizelong="true"
externalizebiginteger="true"
externalizebigdecimal="true">
<classpath>
<pathelement location="classes"/>
</classpath>
<fileset dir="classes">
<include name="test/granite/ejb3/entity/**/*.class"/>
</fileset>
</gas3>
</target>
Se observarem usamos um as3typefactory, entityfactory, remotedestinatiofactory, transformer customizados. Para usar os padrões do GraniteDS basta remover estas tags do build. Na maioria das vezes a configuração do target que gera os as3 acaba ficando muito pequena, acabamos indicando sómente o outputdir e o diretório aonde contem as classes que queremos que ele gere para nós. Ou também indicamos se queremos utilizar os formatos de BigInteger e BigDecimal do GraniteDS.
Como podem ver não é difícil utilizar ant para gerar nossas classes as3 com o auxilio do GAS3. E também temos a documentação oficial que pode ser encontrada aqui.
Todas as opções que vimos no GAS3 e o Plugin para Flash Builder contem na para o ANT. Assim como também estão presentes no FlexMojos que será o tema do nosso próximo post.
Boa Noite Galera.
Vamos iniciar mais um post sobre o GAS3. Hoje vamos falar sobre o plugin para Flash Builder. Para configurar o plugin no Flash Builder vocês irão perceber que não tem segredo. O plugin tem a vantagem de sempre manter nossas entidades Flex sincronizadas com nossas classes Java.
Primeiro baixe o Granite Builder, após o download copie o jar para a pasta plugins/ do seu Flash Builder.
Para começar a utilizar o Plugin do GAS3 basta clicar com o botão direito no projeto e selecionar a opção Add GraniteDS Nature conforme imagem a seguir.
A Tela seguinte é onde selecionamos aonde nossas classes java estão.
A terceira tela é referente aos templates, se observarem as imagens 3 e 4 o ENTITY e o REMOTE_DESTINATION tem uma pequena diferença nos templates das classes base,como podem ver a imagem 3 utiliza o template entityBase.gsp e remoteBase.gsp e na imagem 4 é utilizado o template tideEntityBase.gsp e tideRemoteBase.gsp. Isso é muito interessante pois ele nos deixa livres para utilizar ou não o TIDE como nosso framework no Flex, assim como também nos permite que criemos templates próprios.
Na ultima tela temos algumas configurações referente as Fábricas e o Transformers que o Plugin deve utilizar. Ele também nos permite que utilizemos classes próprias. Se usarmos o tide podemos optar por utilizar a implementação de Long, BigInteger e BigDecimal estes itens são muito uteis se pretendemos manter uma estrutura muito idêntica de tipagem entre o Java e o Flex. Estes itens também serão muito uteis quando usarmos a validação do lado Flex para as entidades(vermos isto em posts futuros).
Em outros posts vou mostrar as diferentes Fabricas que podemos usar com o plugin. E nosso próximo post é sobre a integração do GAS3 com o Apache ANT.
O GAS3 é um poderoso gerador de classes AS3 a partir de suas entidades java. Vou começar a explicar o GAS3 antes mesmo da configuração do GraniteDS, isto porque ele não obriga você a utilizar o GraniteDS. O GAS3 cria duas classes AS3 para uma classe JAVA. Vamos a um simples exemplo para a seguinte classe java.
package org.test;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Welcome implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue
private Integer id;
@Basic
private String name;
public Welcome() {
}
public Welcome(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Teremos as seguintes classes AS3
package org.test {
[Bindable]
[RemoteClass(alias="org.test.Welcome")]
public class Welcome extends WelcomeBase {
}
}
e
package org.test {
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.IExternalizable;
import org.granite.collections.IPersistentCollection;
import org.granite.meta;
use namespace meta;
[Bindable]
public class WelcomeBase implements IExternalizable {
private var __initialized:Boolean = true;
private var __detachedState:String = null;
private var _id:Number;
private var _name:String;
meta function isInitialized(name:String = null):Boolean {
if (!name)
return __initialized;
var property:* = this[name];
return (
(!(property is Welcome) || (property as Welcome).meta::isInitialized()) &&
(!(property is IPersistentCollection) ||
(property as IPersistentCollection).isInitialized()));
}
public function get id():Number {
return _id;
}
public function set name(value:String):void {
_name = value;
}
public function get name():String {
return _name;
}
public function readExternal(input:IDataInput):void {
__initialized = input.readObject() as Boolean;
__detachedState = input.readObject() as String;
if (meta::isInitialized()) {
_id = function(o:*):Number {
return (o is Number ? o as Number : Number.NaN) } (input.readObject());
_name = input.readObject() as String;
}else {
_id = function(o:*):Number {
return (o is Number ? o as Number : Number.NaN) } (input.readObject());
}
}
public function writeExternal(output:IDataOutput):void {
output.writeObject(__initialized);
output.writeObject(__detachedState);
if (meta::isInitialized()) {
output.writeObject(_id);
output.writeObject(_name);
}else {
output.writeObject(_id);
}
}
}
}
O GAS3 gera desta forma para que você possa alterar o arquivo gerado sem se preocupar em perder o que fez em futuras alterações no java e recriação das classes. Sempre que necessário altere a classe principal, o GAS3 só ira gerar ela na primeira vez. Nas próximas gerações ele irá alterar somente a classe XxxBase. O GAS3 também permite que sejam alterados os templates para atender ás suas necessidades. Além das entidades java também podemos usar o GAS3 para gerar as classes de serviço no Flex, o que pode facilitar bastante no desenvolvimento. Nos próximos posts vamos demostrar como utilizar o GAS3 integrado com o Eclipse, Ant e FlexMojos. Também iremos explicar sobre os templates utilizados e sobre as diferentes fábricas que geram as classes AS3.
Page 1 / 2