Red5 + Hibernate

Filed under ActionScript, Hibernate, Java, Red5, Software, Tutorials

This tutorial has been updated. Please check out the new post.

Having followed the growth of the Red5 Media Server[1] from it’s fledgling 0.3 days, I’ve become fairly familiar with its offerings. One of the most frequently asked questions on the Red5 mailing list pertains to database connectivity for user authentication and application security. I’ll attempt to tackle one solution here using Hibernate[2], an object/relational persistence framework. Because Red5 is implemented in Java and makes heavy use of the Spring Framework[3], I’ll be sticking with it for the purposes of this post. Hibernate should be familiar to most Java developers these days, or at least the concept of an ORM framework should be. If not, then you’ve got some homework to do before continuing on here. One other item of note, I won’t be covering MySQL[4] in any detail here, even though it is required for the example code to run. My goal is to cover the integration of the technologies and not to reiterate the numerous introductions already available.

I’ll start by defining a simple Hibernate-backed User object and it’s mapping file. Note: I’ve also included an annotated version of the User object in case you would prefer to run without mapping files.

User.java:

@Entity
@Table(name="users")
public class User
{
	@Id @GeneratedValue
        @Column(name="id", nullable=false)
	private Long id;

	@Column(name="user_name", nullable=false, length=32, unique=true)
	private String userName;

	@Column(name="password", nullable=false)
	private String password;

        ...
}

User.hbm.xml:

<hibernate-mapping package="net.sziebert.red5.adapter.entity">
	<class name="User" table="users">
		<id name="id" type="long" column="id">
			<generator class="native"/>
		</id>
		<natural-id>
			<property name="userName" column="user_name" length="32" not-null="true" unique="true" />
		</natural-id>
		<property name="password" not-null="true" />

		...

	</class>
</hibernate-mapping>

Both are very straight forward. The User class defines fields for a unique identifier and the user’s credentials. It is mapped to the users table via Users.hbm.xml. At this point, a Hibernate session factory and datasource definition are necessary. I’ve defined both in the red5-web.xml file. This file is the means by which Red5 applications wire together resources and define dependencies.

red5-web.xml:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mappingResources">
            <list>
                <value>net/sziebert/red5/adapter/entity/User.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

If you choose to take advantage of Hibernate Annotations, you’ll need to modify your session factory definition to use the AnnotationSessionFactoryBean template.

red5-web.xml:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
            	<value>net.sziebert.red5.adapter.entity.User</value>
            </list>
        </property>
        <property name="annotatedPackages">
            <list>
                <value>net.sziebert.red5.adapter.entity</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

Sticking with patterns most often referenced by other developers/authors, I’ll define a simple data access object and service layer next. Spring offers a template for Hibernate usage which plays very nicely with its transaction management facilities which I’ll cover later.

HibernateApplicationDAO.java:

public class HibernateApplicationDAO extends HibernateDaoSupport implements ApplicationDAO {
	...

	/**
	 * @see ApplicationDAO#getUser(String, String)
	 */
	public User getUser(String user, String pass) {
		log.debug("Retrieving user from the database.");
		// Ask Hibernate to query for the user based upon the specified user/pass.
		Criteria crit = getSession().createCriteria(User.class);
		crit.add(Restrictions.eq("userName", user));
		crit.add(Restrictions.eq("password", pass));
		User u = (User)crit.uniqueResult();
		// Insure that we got something back from Hibernate.
		if (null == u) {
			log.warn("User does not exist for credentials: " + user + "/" + pass);
			throw new ObjectRetrievalFailureException(User.class, user);
		}
		// Return the results.
		return u;
	}
}

ApplicationServiceImpl.java:

public class ApplicationServiceImpl implements ApplicationService {

	...

	/**
	 * @see ApplicationService#getUser(String, String)
	 */
	public User getUser(String user, String pass) throws ServiceException {
		log.debug("Looking up user for user/pass of: " + user + "/" + pass);
		try {
			// Return the result of the data access call.
			return dao.getUser(user, pass);
		} catch (Exception e) {
			log.error("Could not load user!", e);
			throw new ServiceException("Could not load user!", e);
		}
	}
}

You’ll want to take note of the red5-web.xml definition for the ApplicationService implementation. It extends the TransactionProxyFactoryBean definition. By defining the HibernateTransactionManager and this proxy template, we automagically get a Spring-managed Hibernate transaction every time we access the service layer to look up the User. This means that you can skip the boilerplate code for opening a session and starting a transaction.

red5-web.xml:

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref local="transactionManager" />
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="save*">PROPAGATION_REQUIRED</prop>
                <prop key="update*">PROPAGATION_REQUIRED</prop>
                <prop key="delete*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>

    <bean id="applicationDAO" class="net.sziebert.red5.adapter.dao.hibernate.HibernateApplicationDAO">
        <property name="hibernateTemplate" ref="hibernateTemplate" />
    </bean>

    <bean id="applicationService" parent="txProxyTemplate">
        <property name="target">
            <bean class="net.sziebert.red5.adapter.service.impl.ApplicationServiceImpl">
                <property name="applicationDAO" ref="applicationDAO" />
            </bean>
        </property>
    </bean>

I’ll wrap up the Red5 application definitions with the injection of the ApplicationService object into the ApplicationAdapter subclass, defined in red5-web.xml as web.handler. The user authentication process happens within the ApplicationAdapter subclass, Application.java. When a user with valid credentials connects to Red5, the application accepts the connection attempt and allows the user to stay connected. In all other cases, the connection attempt is rejected and the connection is closed.

red5-web.xml:

<bean id="web.handler" class="net.sziebert.red5.adapter.Application" singleton="true">
    <property name="applicationService" ref="applicationService" />
</bean>

Application.java:

public class Application extends ApplicationAdapter {

	...

	public boolean roomConnect(IConnection conn, Object[] params) {
		log.debug("New connection attempt from " + conn.getRemoteAddress() + "...");
		// Parse the user/pass out of the connection parameters
		String userName = (String)params[0];
		String password = (String)params[1];
		// Get the User object for this connection
		User user = null;
		// Insure that we have received the proper set of parameters.
		if (StringUtils.isNotBlank(userName) &&
				StringUtils.isNotBlank(password)) {
			user = authenticate(userName, password);
		}
		// If we got a valid user object, then allow the connection. Otherwise,
		// the user could not be found or something bad happened. In either
		// case, we do not want to allow the connection.
		return user != null;
	}	

	...
}

The ActionScript side of things is also very straight forward. I’ve created a simple client SWF which passes the specified user credentials as part of the NetConnection.connect() call and defines callback functions for handling the results of the connection attempt. The results are also logged to a TextArea instance for convenient review.

HibernateController.as:

class net.sziebert.ria.HibernateController {

	...

	/**
	 * Initialize the connection to the Red5 media server.
	 */
	private function initConnection(user:String, pass:String):Void {
		trace(className + ": initConnection(" + user + ", " + pass + ")");
		// Create the new connection object.
		conn = new Red5Connection({server:"localhost", app:"hibernate", room:"test"});
		// Add the NetConnection event listeners
		conn.addEventListener("onConnect", Delegate.create(this, onConnect));
		conn.addEventListener("onReject", Delegate.create(this, onReject));
		conn.addEventListener("onClose", Delegate.create(this, onClose));
		conn.addEventListener("onFail", Delegate.create(this, onFail));
		// Connect to the remote server
		log("Connecting to " + conn.getServerUrl());
		conn.connect(conn.getServerUrl(), user, pass);
	}

	...
}

That’s more or less it. Take a look over the example code, drop it into your Red5 install and let’er rip. Keep in mind that you’ll need to configure MySQL to allow Red5 to talk to it. (Notes on this can be found in the README file with the examples.) You’ll also need to add your user data to the tables once Hibernate has generated the schema.

This tutorial has been updated. Please check out the new post for the latest source code bundle.

1. http://osflash.org/red5 For those that aren’t up on Red5, it is a robust Flash Media Server alternative written entirely in Java that supports multi-user video chat, video streaming and real-time, multi-player gaming.

2. http://hibernate.org Hibernate lets you develop persistent classes following object-oriented idiom – including association, inheritance, polymorphism, composition, and collections. It allows you to express queries in its own portable SQL extension (HQL), as well as in native SQL.

3. http://springframework.org Spring is a layered Java/JEE application framework founded on the simple concepts that any tool should be a pleasure to use, that your application code should not depend on Spring APIs and that it should not compete with existing solutions, but should foster integration.

4. http://mysql.com The MySQL database has become the world’s most popular open source database because of its consistent fast performance, high reliability and ease of use.

Liked this post? Share it with others:
  • Digg
  • del.icio.us
  • Facebook
  • DZone
  • email
  • LinkedIn
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

7 Comments

  1. Posted August 25, 2007 at 4:25 am | Permalink

    Hi, Good to see your tutorial, We have done this long time back, when we started with Red5, Our application is pretty much stable and in the application hibernate is heavily being used.

  2. NAHON
    Posted October 8, 2007 at 7:43 am | Permalink

    Can you make a little exemple for calling other service ?

  3. Posted October 22, 2007 at 4:49 pm | Permalink

    @NAHON:

    I’ll see about putting together an example for you. Is there anything specific you’d like to see included besides calling a remote service?

  4. Brian
    Posted November 23, 2007 at 1:05 pm | Permalink

    Great post! I think a nice follow-up would be to show integration with JAAS declarative authorization.

  5. Posted January 16, 2008 at 11:00 am | Permalink

    Very nice this tutorial! Congratulations!

    I’m finding by tutorials wich demonstrate how do work with remoting objects using Adobe Flex3 and how do work with shared objects, boths using the red5 in server.

    Have any information about this?

    Thanks, regards!
    (sorry, i’m brazilian and my english is half poor)

  6. Posted September 15, 2008 at 9:26 am | Permalink

    Can you please update this tutorial to red5 0.7 , many of us come from AS only background and doing database operations directly(no-brain-killing-way using async remoting) is a huge(GIGANTIC_TITANIC) step forward for RTMP applications.

  7. Posted September 16, 2008 at 10:04 am | Permalink

    @ion:

    I’m currently out of town on business, however when I return I’ll update the example application to both 0.7.x and the latest from trunk.

2 Trackbacks

  1. [...] followed the growth of the Red5 Media Server[1] from it’s fledgling 0.3 days, I’ve become fairly familiar with its offerings. One of the most [...]

  2. By Carl Sziebert » Red5 + Hibernate Revisited on January 30, 2009 at 3:13 pm

    [...] and is much needed in this case. My apologies for not tending to the garden sooner. While the original article illustrated a simple method for integrating Red5[1] and Hibernate[2], by today’s standards [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*