Entries for month: December 2008
Before You Begin:
Before we dive right into the configuration, there are a few caveats that you must know about setting up RTMP Tunneling (RTMPT). First, since it binds to port 80 as a proxy to port 8080, you need to make sure those two ports are available on the IP address to which you are binding. Most J2EE servers run their default web instance on 8080, so you may have to go into your server.xml file to change that. Changing it to 8081 should work fine if you're not using that port for something else. Also, and this is very important, if you are on a UNIX type system, you must run LCDS as root. Only the root account can bind to port 80, so you must be running as root to use RTMPT on a UNIX system.
Setting Up A RTMPT Destination
In your services-config.xml file, you will need to define a new destination for your RTMPT endpoint. I like to use the current RTMP endpoint as a starting point because it's basically setup the way we need it to be, from the onset. Modify your services-config.xml file to include this:
<channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel">
<endpoint url="rtmp://{server.name}:2038" class="flex.messaging.endpoints.RTMPEndpoint"/>
<properties>
<idle-timeout-minutes>20</idle-timeout-minutes>
<!-- for deployment on WebSphere, must be mapped to a WorkManager available in the web application's jndi context.
<websphere-workmanager-jndi-name>java:comp/env/wm/MessagingWorkManager</websphere-workmanager-jndi-name>
-->
</properties>
</channel-definition>
<channel-definition id="my-rtmpt" class="mx.messaging.channels.RTMPChannel">
<endpoint url="rtmpt://{server.name}:80" class="flex.messaging.endpoints.RTMPEndpoint"/>
<properties>
<idle-timeout-minutes>20</idle-timeout-minutes>
<!-- for deployment on WebSphere, must be mapped to a WorkManager available in the web application's jndi context.
<websphere-workmanager-jndi-name>java:comp/env/wm/MessagingWorkManager</websphere-workmanager-jndi-name>
-->
</properties>
</channel-definition>
You can see, we're using the same channel, we've just changed the URI to be rtmpt://{server.name}:80 instead of rtmp://{server.name}:2038 and give the new channel an id of my-rtmpt. Now that we have our channel defined, let's take a look at how we would use it.
Enabling Failover To RTMPT
Here, we're going to look at a sample destination in messaging-config.xml. This is a simple destination that uses three channels. The first connection attempt will be via RTMP. If that cannot be connected, then an attempt will be made to failover to RTMPT. I like to have a third option in there as well, just as a last resort in case all hell breaks loose with RTMP, for whatever reason. I typically use AMF Polling for this final option. It comes pre-defined for you in the services-config.xml file. This is the setup you need for a messaging destination with failover to RTMPT. Same goes for Data Mangement setup, as seen below. Typically, you do not want to use RTMP or RTMPT for remoting calls because these channels are really a bit overkill for such simple calls.
Sample Messging Destination, configured in messaging-config.xml
<destination id="failoverMessagingSampleDestination">
<channels>
<channel ref="my-rtmp"/>
<channel ref="my-rtmpt"/>
<channel ref="my-polling-amf"/>
</channels>
</destination>
Sample Data Management Destination, configured in data-management-config.xml
<destination id="fdms-tutorial-product">
<adapter ref="java-dao" />
<properties>
<source>flex.tutorial.fdms.ProductAssembler</source>
<scope>application</scope>
<metadata>
<identity property="productId"/>
</metadata>
</properties>
<channels>
<channel ref="my-rtmp"/>
<channel ref="my-rtmpt"/>
<channel ref="my-polling-amf"/>
</channels>
</destination>
RTMPT failover can be very useful for you when your users have port 2038 blocked, but the setup is not automatic. It takes some minor configuration and some care to setup the failover channel, but can really become quite handy when you need it. Keep in mind, since we're talking about RTMP and RTMPT, this only applies to LCDS. The protocol is not available in BlazeDS, but the basic failover methodology is, albeit with other channels.
Tags:
ColdFusion · Flex · BlazeDS · Linux · Universal Mind · AIR
I've been having some issues with my hosting provider. I thought about naming names, but I'll pass on that, for now. I finally got fed up with the issues that I was having, so I decided to look for alternates:
I came across the folks at Slicehost. After some asking around and doing research on my own, I figured that I'd give them a shot. It sure is a heck of a deal. At less than 1/2 the cost of the VPS I had at the other provider, I got the same power and flexibility that I had with the other provider. The only difference is that I had to do all the DB and ColdFusion setup myself.
No big deal, I can handle that. I had to get back into linux a bit, but that was like riding a bike. I didn't even cheat and use webmin!
So, now I'm hosted at Slicehost and we'll see how this goes....
Tags:
Apache · ColdFusion · BlazeDS · Linux
In listening to people complain about different languages (MXML, AS3, Java, ColdFusion, C#, etc), most everything someone lists as what they love/hate about the language is a function of the IDE. The IDE, in turn, is usually built around the functionality of the language. I guess what I want to know from you is, would you be less down on a language / technology if the IDE was better to work with? In Java, if you don't like Eclipse, you can go to IntelliJ, and vice versa. The .NET languages tie you into Visual Studio, for the most part. Flex, until recently, was limited to FlexBuilder. Anyway, your thoughts on the issue would be appreciated.
Does the IDE influence how much you love / hate a language?
Tags:
Java · ColdFusion · Flex · Silverlight · Ruby on Rails · Universal Mind · XML
Click Here To View The Sample
I've spent a lot of time over the past couple of months digging deeply into Silverlight (more about why in a future post). One of thing things that has constantly frustrated me with Silverlight is the fact that you're pretty much limited to some sort of text based transfer protocol, be it XML, JSON, SOAP, whatever. I guess Adobe has spoiled me with using AMF for everything. Well, after some digging, I came across FluorineFx, an open source library that allows for remoting within the .NET framework.
Getting it setup is pretty simple. You just run the installer, then add a reference to the FluroineFx.dll in your project. Once that was all set, it took me a bit of digging to get the right setup for remoting. I had my destination, my cfc path, my method name, all the stuff needed to get this working right. So, I dove into the samples, and applied them to my ColdFusion remoting setup. The results: an AMF call to ColdFusion that returned data back properly. Let's take a look at the code:
Page.xaml:
<UserControl x:Class="SilverlightColdFusion.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="300">
<Grid x:Name="LayoutRoot" Background="AliceBlue">
<StackPanel Orientation="Vertical">
<Button x:Name="factButton" Content="Click To Load Fact" Height="25" Width="75" Click="loadBtn_Click"/>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="15">
<TextBlock x:Name="tLabel" Text="Your Fact: "/>
<TextBlock x:Name="tFact" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
This is a pretty straightforward, no nonsense XAML file. We've basically got a button that will fire our call to ColdFusion and a TextBlock that will display the result of the call.
Page.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using FluorineFx;
using FluorineFx.Net;
using FluorineFx.Messaging.Api;
using FluorineFx.Messaging.Api.Service;
using FluorineFx.AMF3;
namespace SilverlightColdFusion
{
public partial class Page : UserControl
{
NetConnection _netConnection;
public Page()
{
InitializeComponent();
_netConnection = new NetConnection();
_netConnection.ObjectEncoding = ObjectEncoding.AMF3;
_netConnection.NetStatus += new NetStatusHandler(_netConnection_NetStatus);
_netConnection.Connect("http://www.infoaccelerator.net:80/flex2gateway");
}
void _netConnection_NetStatus(object sender, NetStatusEventArgs e)
{
string level = e.Info["level"] as string;
}
private void loadBtn_Click(object sender, RoutedEventArgs e)
{
_netConnection.Call("my-cfamf","ColdFusion","chuckFacts.FactService","getCurrentFact",new GetFactsHandler(this));
}
public class GetFactsHandler : IPendingServiceCallback
{
Page _page;
public GetFactsHandler(Page page) {
_page = page;
}
public void ResultReceived(IPendingServiceCall call) {
string fact = call.Result as string;
_page.Bind(fact);
}
}
public void Bind(string fact) {
Dispatcher.BeginInvoke(delegate()
{
tFact.Text = fact;
});
}
}
}
Open up your favorite AMF debugging proxy, be it Charles or Service Capture and take a look at the traffic passing back and forth between the sample application below and the server. You'll see perfectly formatted AMF going back and forth between Silverlight and ColdFusion. My next post will be a follow-up on how to send typed objects across the wire to Silverlight.
Click Here To View The Sample
Tags:
ColdFusion · Flex · Silverlight · Universal Mind
The Problem:
When using Hibernate as your persistence layer, it allows you to create lazy collections. This is nice because lazy collections can speed up your application, and in theory, you only access the data you want to access. Simple enough. The problem lies however in talking to Flex applications, specifically via BlazeDS or LiveCycle DS. What happens is the fault of the serializers. When you make a request, via mx:RemoteObject, to the server, and you are accessing a lazy collection, when the serializer attempts to turn the data from Java to AMF, it touches everything. This "unwelcome touching" by the serializer, in turn, triggers all of your lazy loading to fire, therefore rendering your lazy collections and the effort to set them up, useless. So, with that in mind, how do we pass back only the data we want to pass back?
The Solution
Let's consider the following value objects:
package com.universalmind.samples.lazyloadIssue;
import org.hibernate.annotations.Entity;
import java.util.UUID;
/**
* Copyright (c) 2008 Universal Mind Inc.
* Created by IntelliJ IDEA.
* Created By: Andrew Powell
* Date: Dec 9, 2008
* Time: 11:44:37 AM
*/
@Entity
public class Item implements AMFSerializable {
private UUID id;
private String sku;
private String name;
private String description;
public Item() {
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getSku() {
return sku;
}
public void setSku(String sku) {
this.sku = sku;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Item cloneForAMF() {
Item item = new Item();
item.setDescription(description);
item.setId(id);
item.setName(name);
item.setSku(sku);
return item;
}
}
package com.universalmind.samples.lazyloadIssue;
import org.hibernate.annotations.Table;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.FetchType;
import java.util.UUID;
import java.util.Date;
import java.util.ArrayList;
/**
* Copyright (c) 2008 Universal Mind Inc.
* Created by IntelliJ IDEA.
* Created By: Andrew Powell
* Date: Dec 9, 2008
* Time: 11:43:09 AM
*/
@Entity
public class Order implements AMFSerializable {
private UUID id;
private Date date;
private ArrayList items;
public Order() {
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@ManyToMany(targetEntity=Item.class,fetch= FetchType.LAZY)
public ArrayList getItems() {
return items;
}
public void setItems(ArrayList items) {
this.items = items;
}
public Order cloneForAMF() {
Order clone = new Order();
clone.setDate(date);
clone.setId(id);
return clone;
}
}
These two value objects are related in the sense that there is a many-to-many relationship between Order and Item. An Order can have many Item instances and a Item can belong to many Order instances. There is another piece in there as well. Both of these implement the AMFSerializable interface, listed below:
package com.universalmind.samples.lazyloadIssue;
import java.io.Serializable;
/**
* Copyright (c) 2008 Universal Mind Inc.
* Created by IntelliJ IDEA.
* Created By: Andrew Powell
* Date: Dec 9, 2008
* Time: 12:16:37 PM
*/
public interface AMFSerializable extends Serializable {
public Object cloneForAMF();
}
This interface extends java.io.Serializable which is needed for AMF serialization. It also adds one method: cloneForAMF(). This method allows you to create a copy of the object that is free from Hibernate proxies and only contains the data that you want to send back to Flex. The next trick, however, is invoking that method once your business logic completes.
Let's say we have a simple, Hibernate and Spring enabled, data access object:
package com.universalmind.samples.lazyloadIssue;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import java.util.*;
/**
* Copyright (c) 2008 Universal Mind Inc.
* Created by IntelliJ IDEA.
* Created By: Andrew Powell
* Date: Dec 9, 2008
* Time: 11:42:55 AM
*/
public class OrderDAO extends HibernateDaoSupport {
@SuppressWarnings("unchecked")
public ArrayList getTodaysOrders(Date yesterday, Date tomorrow){
DetachedCriteria criteria = DetachedCriteria.forClass(Order.class);
criteria.add(Restrictions.gt("date",yesterday));
criteria.add(Restrictions.lt("date",tomorrow));
return (ArrayList) this.getHibernateTemplate().findByCriteria(criteria);
}
}
The method we're going to call, getTodaysOrders is going to return an ArrayList, given a set of criteria. Pretty simple. Between the time this method executes and the time it hits the serializer, within the RemoteObject call, we want to prep our data with the cloneForAMF() method that we created on our value objects. Since we're using Spring, we can leverage the power of Aspect-Oriented Programming to accomplish this task. In order to accomplish this with AOP, we need to implement advice both before and after the method in question. Implementing this "around advice" is easy using the MethodInterceptor class, as shown below:
package com.universalmind.samples.lazyloadIssue;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.util.ArrayList;
/**
* Copyright (c) 2008 Universal Mind Inc.
* Created by IntelliJ IDEA.
* Created By: Andrew Powell
* Date: Dec 9, 2008
* Time: 12:12:37 PM
*/
public class PreSerializationInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if(methodInvocation.getMethod().getName() == "getTodaysOrders"){
ArrayList persistentCollection = (ArrayList) methodInvocation.proceed();
ArrayList serializedCollection = new ArrayList();
for(Order order : persistentCollection){
if(order instanceof AMFSerializable) serializedCollection.add(order.cloneForAMF()); }
return serializedCollection;
}
else{
return methodInvocation.proceed();
}
}
}
This code will first make sure we're executing the method we want to clean the results of, execute the method using methodInvocation.proceed(), and then we will do some "post-processing" of the method results to only return what we want to be serialized to Flex. We must also create a new collection, as the collection that is returned is aware that it's a Hibernate lazy-collection (via proxy). After we create the new collection, if the members of the persistent collection implement the AMFSerializable interface, we will call the cloneForAMF() method and add the resulting object to our new non-persistent collection. This collection then gets returned and serialized by the AMF serializer. There are now lazy-loaded collections triggered because the collection we are passing back to the serializer is blissfully unaware of Hibernate or any lazy-collection.
The drawback to this method is that every time you want to get an object's collection, you must make another round-trip to the server. These trips can be less data passed across, but be aware that this must happen if you want to retrieve the lazy collections. Those calls must also be via another method you create, you cannot just load the same objects up and expect the collections to come back by themselves. That defeats the purpose.
I must admit that this does feel like a bit of a hack instead of a more elegant solution like dpHibernate, but sometimes you don't want to mess around with custom adapters on the Java side and special configs on the Flex side. This allows you to implement Hibernate and bypass lazy loading with minimal impact to your value objects and retrieval methods on the AS side. So, in that sense, I think this solution has its place and can be valuable to you on your Flex / Java projects.
Tags:
LiveCycle ES · Java · Flex · BlazeDS · Spring · Universal Mind · Hibernate
December 04, 2008 · 1 Comment
Dave Meeker, Adam Flater, and myself were interviewed a while ago about Merapi for The Flex Show. The episode has finally posted and is available for your enjoyment. It makes a good listen on mass transit and will have you amused and amazed.
Tags:
Merapi · Java · FlexCamp · Flex · BlazeDS · Adobe · Universal Mind · AIR · WebNext
December 03, 2008 · 1 Comment
There are tickets still available and it's dirt cheap for a day full of great content. Head over to http://www.flexcampboston.com and register today!
Tags:
Merapi · Java · FlexCamp · Flex · Conferences · BlazeDS · SpatialKey · Adobe · WebNext · Speaking