You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.
| Online: | 12 Members of 9401 |
| Forum: Seam Users |
16. Oct 2008, 17:53 America/New_York | Link |
Hi all,
We have just released Granite Data Services 1.1.0, a LGPL alternative to Adobe LiveCycle Data Services, which includes the first stable release of the Tide integration package for Seam.
Tide aims at providing to the Flex client a transparent integration with server Seam components, with a client programming model similar to Seam remoting in JavaScript. After an initial setup, it requires close to no configuration during development and makes developing Flex applications very natural for people used to Seam/JSF/facelets.
Tide integrates with most features of Seam : component bijection, events, conversations, security with Identity component. For example, a Tide Flex client component can get injected with values that have been outjected by a server component, or can register as a listener for Seam events raised by server components (even asynchronously thanks to the integration with the GraniteDS Gravity push library).
Finally Tide provides advanced data management features. It allows to use Seam Query components as paged data providers for Flex UI components (DataGrids, Lists...) and can transparently initialize lazy collections. This GDS release includes Granite Eclipse builder, an Eclipse plugin which can automatically generate the ActionScript entity classes from the Java JPA/EJB3 model.
You can download two sample projects : - a port of Seam booking to Flex (without any modification on the original Seam components) - a simple Address Book, demonstrating simple CRUD functionality with data paging and lazy loading of collections (using only basic Seam Home and Query components)
Here is a small example from the hotel booking project showing the use of context injection in Flex.
Seam component :
@Stateful
@Name("hotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class HotelSearchingAction implements HotelSearching {
@PersistenceContext
private EntityManager em;
private String searchString;
private int pageSize = 10;
...
@DataModel
private List<Hotel> hotels;
public void find() {
page = 0;
queryHotels();
}
...
private void queryHotels() {
hotels = em.createQuery("select h from Hotel h where lower(h.name) like #{pattern} or lower(h.city) like #{pattern} or lower(h.zip) like #{pattern} or lower(h.address) like #{pattern}")
.setMaxResults(pageSize)
.setFirstResult( page * pageSize )
.getResultList();
}
public List<Hotel> getHotels() {
return this.hotels;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
@Factory(value="pattern", scope=ScopeType.EVENT)
public String getSearchPattern() {
return searchString==null ? "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
}
public String getSearchString() {
return searchString;
}
public void setSearchString(String searchString) {
this.searchString = searchString;
}
...
}
Flex/Tide component :
[Bindable]
[Name("hotelsCtl", restrict="true")]
public class HotelsCtl {
// The [In] annotation indicates that this variable will be injected with a proxy of the Seam component named 'hotelSearch'.
[In]
public var hotelSearch:Object;
// The [Observer] annotation indicates that this method is an event handler. It will listen for events raised by other components or by the UI.
[Observer("searchForHotels")]
public function searchForHotels(searchString:String, pageSize:Number):void {
// Set the variables to inject in the hotelSearch component
hotelSearch.searchString = searchString;
hotelSearch.pageSize = pageSize;
// Remote call of the 'find' method of the 'hotelSearch' Seam component
hotelSearch.find();
}
}
Flex/Tide UI :
<mx:Application initialize="tideContext.mainAppUI = this;">
...
<mx:Script>
<![CDATA[
// Initialize the Tide context
[Bindable]
private var tideContext:Context = Seam.getInstance().getSeamContext();
// Register our client component
Seam.getInstance().addComponents([HotelsCtl]);
[Bindable]
public var pageSizes:ArrayCollection = new ArrayCollection([5, 10, 20]);
// The [In] annotation indicates that this 'hotels' variable will be injected with the Seam component/context variable named 'hotels' whenever a Seam component outjects it
[Bindable] [In]
public var hotels:ArrayCollection;
]]>
</mx:Script>
...
<mx:HBox direction="horizontal" paddingBottom="5" paddingLeft="0" paddingRight="0" paddingTop="5" width="100%">
<mx:TextInput id="txtQueryString" width="200" color="#C0C0C0" />
<mx:ComboBox id="cmbResultSize" width="58" x="0" y="0" upSkin="myCombobox" downSkin="myComboboxDown" overSkin="myComboboxOver" disabledSkin="myComboboxDisabled" dataProvider="{pageSizes}"/>
<mx:Spacer width="5"/>
<!-- Raises an event which will trigger our Observer client component -->
<mx:Button label="Search" x="0" y="0"
click="dispatchEvent(new TideUIEvent('searchForHotels', txtQueryString.text, Number(cmbResultSize.selectedLabel)));"
upSkin="myButton" downSkin="myButtonDown" overSkin="myButtonOver" disabledSkin="myButtonDisabled"/>
</mx:HBox>
...
<mx:VBox height="360" width="100%" styleName="boxBordersSubbgSkin">
<mx:Box backgroundColor="#e2e5d5" borderColor="#c0c0c0" styleName="tabssub" width="100%">
<mx:DataGrid id="hotelSearchResults" rowCount="6" width="100%" dataProvider="{hotels}">
<mx:columns>
<mx:DataGridColumn itemRenderer="components.ImageRender" width="25" />
<mx:DataGridColumn dataField="name" width="175" />
<mx:DataGridColumn dataField="address" width="175" />
<mx:DataGridColumn dataField="city" width="100" />
<mx:DataGridColumn dataField="state" width="75" />
<mx:DataGridColumn dataField="zip" width="50" />
</mx:columns>
</mx:DataGrid>
</mx:Box>
</mx:VBox>
...
</mx:Application>
You can get more information at http://www.graniteds.org.
I tried to run the examples but ran into several problems. I couldn't get any of them run in eclipse. Switching to the commandline, I made the booking app run by correcting the import.sql file and manually deploying the datasource. The other app ran, but it gave a number of errors like the following:
javax.servlet.ServletException: Not in a valid Comet configuration (use an APR or NIO connector) at org.granite.gravity.tomcat.AbstractCometProcessor.service(AbstractCometProcessor.java:192) at javax.servlet.http.HttpServlet.service(HttpServlet.java:803) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:182) at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446) at java.lang.Thread.run(Thread.java:613)16:04:08,016 ERROR [AMFMessageFilter] AMF message error java.lang.RuntimeException: Could not read externalizable object: [] at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:379) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:389) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Array(AMF3Deserializer.java:253) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:116) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:389) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.messaging.amf.io.AMF0Deserializer.readAMF3Data(AMF0Deserializer.java:324) at org.granite.messaging.amf.io.AMF0Deserializer.readData(AMF0Deserializer.java:376) at org.granite.messaging.amf.io.AMF0Deserializer.readArray(AMF0Deserializer.java:239) at org.granite.messaging.amf.io.AMF0Deserializer.readData(AMF0Deserializer.java:362) at org.granite.messaging.amf.io.AMF0Deserializer.readBodies(AMF0Deserializer.java:155) at org.granite.messaging.amf.io.AMF0Deserializer.<init>(AMF0Deserializer.java:94) at org.granite.messaging.webapp.AMFMessageFilter.doFilter(AMFMessageFilter.java:80) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:182) at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446) at java.lang.Thread.run(Thread.java:613) Caused by: java.lang.RuntimeException: Could not read externalized object: test.granite.ejb3.entity.Person@650d29e0 at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:359) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:389) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Array(AMF3Deserializer.java:253) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:116) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at flex.messaging.io.ArrayCollection.readExternal(ArrayCollection.java:64) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:375) ... 34 more Caused by: java.lang.RuntimeException: Could not create instance of: test.granite.ejb3.entity.Person$Salutation at org.granite.messaging.amf.io.util.DefaultActionScriptClassDescriptor.newJavaInstance(DefaultActionScriptClassDescriptor.java:83) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:345) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.hibernate.HibernateExternalizer.readExternal(HibernateExternalizer.java:107) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:355) ... 43 more Caused by: java.lang.InstantiationException: test.granite.ejb3.entity.Person$Salutation at java.lang.Class.newInstance0(Class.java:335) at java.lang.Class.newInstance(Class.java:303) at org.granite.util.ClassUtil.newInstance(ClassUtil.java:47) at org.granite.messaging.amf.io.util.DefaultActionScriptClassDescriptor.newJavaInstance(DefaultActionScriptClassDescriptor.java:81) ... 48 more 16:04:08,018 ERROR [[AMFMessageServlet]] Servlet.service() for servlet AMFMessageServlet threw exception java.lang.InstantiationException: test.granite.ejb3.entity.Person$Salutation at java.lang.Class.newInstance0(Class.java:335) at java.lang.Class.newInstance(Class.java:303) at org.granite.util.ClassUtil.newInstance(ClassUtil.java:47) at org.granite.messaging.amf.io.util.DefaultActionScriptClassDescriptor.newJavaInstance(DefaultActionScriptClassDescriptor.java:81) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:345) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.hibernate.HibernateExternalizer.readExternal(HibernateExternalizer.java:107) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:355) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:389) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Array(AMF3Deserializer.java:253) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:116) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at flex.messaging.io.ArrayCollection.readExternal(ArrayCollection.java:64) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:375) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:389) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Array(AMF3Deserializer.java:253) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:116) at org.granite.messaging.amf.io.AMF3Deserializer.readAMF3Object(AMF3Deserializer.java:389) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:118) at org.granite.messaging.amf.io.AMF3Deserializer.readObject(AMF3Deserializer.java:87) at org.granite.messaging.amf.io.AMF0Deserializer.readAMF3Data(AMF0Deserializer.java:324) at org.granite.messaging.amf.io.AMF0Deserializer.readData(AMF0Deserializer.java:376) at org.granite.messaging.amf.io.AMF0Deserializer.readArray(AMF0Deserializer.java:239) at org.granite.messaging.amf.io.AMF0Deserializer.readData(AMF0Deserializer.java:362) at org.granite.messaging.amf.io.AMF0Deserializer.readBodies(AMF0Deserializer.java:155) at org.granite.messaging.amf.io.AMF0Deserializer.<init>(AMF0Deserializer.java:94) at org.granite.messaging.webapp.AMFMessageFilter.doFilter(AMFMessageFilter.java:80) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:182) at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446) at java.lang.Thread.run(Thread.java:613)Aie, I should have added more information for installing the sample projects :
For both projects, there is an ant build file which needs some settings in env.properties :
FLEX_HOME should be set to the Flex SDK home :
For a Flex Builder 3 setup, this is in general something like this :
FLEX_HOME=C:/Program Files/Adobe/Flex Builder 3 Plug-in/sdks/3.0.0 FLEX_TASKS_JAR=${FLEX_HOME}/ant/lib/flexTasks.jarOtherwise :
FLEX_HOME=C:/flex_sdk_3 FLEX_TASKS_JAR=${FLEX_HOME}/ant/lib/flexTasks.jarJBOSS_HOME should be set to your jboss home directory
For seam-booking, just run the 'deploy' target of the ant build file, it will deploy the ear, datasource and import.sql.
For the address book, there are a few more things needed :
The APR or NIO connector is used for handling asynchronous servlets and should be enabled for Tomcat (6.0.14 minimum). Just put the Tomcat native dll from here in C:\windows\system32 or in jboss/bin. Other better configurations are possible : tomcat docs are here.
The security configuration of this project uses JAAS, so you might add defaultUsers.properties and defaultRoles.properties in jboss/server/default/conf.
defaultUsers.properties:
defaultRoles.properties :
Hope this helps.
You should add the NIO information to the README. Everything else was covered there.
On the booking example, the build.xml file is looking for the import.sql file in the classes directory, but it is in the ejb3 directory. (the build.xml also seems to be looking for the ds.xml in the wrong directory too) Maybe there is supposed to a target that copies these files there?
On the address book example, I discovered that the error I posted above comes only when I select a select a Salutation. When I don't, the app appears to work to some degree. Would not having the NIO connector installed be likely to cause the rest of the app to behave strangely? I'm not seeing any other exceptions generated beyond the first one.
This is very strange. I've rebuilt the addressbook sample from scratch from the provided url and it works fine with a jboss 4.2.3 instance. Even with APR/NIO not installed, the parts which do not depend on it (almost everything) do work correctly. The issue with Person$Salutation is likely to happen if there is no META-INF/granite-config.properties in granite-tide.jar, which was the case in some recent nightly builds.
The build file for the booking sample will be fixed in the next snapshots. It was indeed broken when not executed inside eclipse.
Thanks for taking the time of trying it.
I'm a total Flex neophyte so I know very little but what you've got showing there looks pretty darn cool. It has total wow factor.
Thanks for posting this, definitely something interesting to check out. :)
Beware of programmers carrying screwdrivers
This should be what jboss bundles with Seam, not jsf/RichFaces. I dont want to be unkind but richfaces is so far from anything you would want to call production worthy that i am amazed jboss put their name to it.
Thanks for these encouraging comments !
I'll just add a quick news to announce GraniteDS 1.2.0 RC1 with full support for Seam 2.1 (there were API changes that were incompatible with GDS 1.1).
Has anyone done a GraniteDS / Flamingo comparison?
Can the updated example using GraniteDS and Seam 2.1 (or latest) be posted and/or made available for download? I'm new to Seam/GraniteDS so I doubt I can make the changes manually.
Hi all,
GraniteDS 2.0 beta 2 has just been released and is available for download here.
It brings a lot of bug fixes and improvements and Seam 2.1 is now the recommended version when integrating with Flex.
The two GraniteDS/Seam example projects have been completely rewritten and demonstrate most functionalities of the integration.
These examples can be deployed in a JBoss 4.2.2/4.2.3 instance (please read examples/README.txt for instructions, in particular Tomcat APR/NIO configuration).
William
Do you have any idea about the time frame for GraniteDS 2.0 GA? If I want to run Seam & GraniteDS on Glassfish, do I need special instructions?
Regards.
There will be at least one RC before the final release, so you can expect a final 2.0 in about 1 or 2 months.
We have not finished the documentation about GraniteDS / Seam on GlassFish, but if you have a working setup of your Seam app on GlassFish, you've done the most difficult part. There is no special configuration for GraniteDS on GF, except choosing between granite-hibernate.jar, granite-toplink.jar and granite-eclipselink.jar depending on the JPA implementation you plan to use.
Hi,
why does Seam not support Flex directly?
http://seamframework.org/Community/DirectSeamSupportForFlex
Michael
Hi William,
How would you compare GraniteDS with Flamingo? Knowing that I'm using Flamingo right now, why would I switch to GraniteDS?
Thanks,
I'm not sure I can do a really fair comparison, but at least I can give a list of GraniteDS features that I think Flamingo does not have. I guess the Flamingo developers will correct me if I say something wrong :)
<mx:Button ... visible="{identity.hasRole('admin')}"/>Other features are not particularly tied to Seam but greatly simplify building Flex applications with a Seam/Spring backend :
Most of these features are demonstrated in the seam-flexbooking and graniteds-tide-seam examples in the latest GDS 2.0 beta 2 distribution here.
To be honest there are also Flamingo features that GraniteDS does not provide (or not completely) :
identity.username = "Barack"; identity.password = "Obama"; identity.loggedIn; // Calling the getter before the remote call indicates GDS that it should get the value after the call identity.login(loginResult); private function loginResult(event:TideResultEvent):void { if (identity.loggedIn) // The property has been correcly retrieved from the server ... }At the end, I think the best way to have a more precise idea is to compare the implementations of the Seam booking example in Flex with GraniteDS and Flamingo. You can easily check that there is a lot less Flex code and configuration with GDS.
Regards,
William
What's the advantage of Granite over Exadel's Flamingo? Any differences?
Found some answers: http://seamframework.org/Community/FlamingoBringsFlexToYourSeamApp
Hello,
see my tutorial for integrating Seam and Flex with FlamingoDS here.
Regards Michael
Hi all,
GraniteDS 2.0.0.GA has just been released and is available for download here.
There are once again a lot of bug fixes and improvements in the Flex/Seam integration. The best way to get started is to have a look to the Flex port of the Seam hotel booking example that is available in the examples folder.
Regards,
William