Home > Guides > Programming > Java

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Stateless Session Beans

Last updated Jan 20, 2006.

In previous manifestations of the EJB specification, Stateless Session Beans were defined through a combination of a local and/or remote interface, a home interface, and a bean implementation class implemented the javax.ejb.SessionBean interface. Furthermore, there were deployment descriptors to build to define both the Stateless Session Bean itself, as well as any beans or resources that it referenced.

The benefits were worth the work, but it was like changing the alternator on my car. It is not worth it to pay someone else a couple hundred dollars to do the job, but it sure ain't fun!

In EJB3, a Stateless Session Beans, like an Entity Bean, is a plain-old Java object (POJO) that include an annotation that tells the EJB container to treat it as a Stateless Session Bean. It still does require a local and/or remote interface, but unlike previous versions, this interfae does not extend javax.ejb.EJBObject, but rather is a plain-old interface. Furthermore, because Stateless Session Beans run inside the EJB container, resources and beans can be automatically injected into the Stateless Session Bean using annotations. No longer do you have to connect to the JNDI server and locate an object (and of course give it a JNDI server agnostic name in your deployment descriptor).

There are two requirements for the Stateless Session Bean class:

  • It must implement the Local/Home interface
  • It must tell the EJB container that it is a Stateless Session Bean by including the @Stateless annotation

The @Stateless annotation is defined in listing 13.

Listing 13. @Stateless Annotation

@Target(TYPE) @Retention(RUNTIME)
public @interface Stateless {
 String name() default "";
 String mappedName() default "";
 String description() default "";
}

The name attribute identifies the bean, but defaults to the unqualified name of the bean class itself. In practice, this attribute should be omitted unless you have a strong reason to override it.

The mappedName attribute is a product-specific name to which the bean should be mapped. Beans using the mappedName attribute may not be portable; we'll learn more as more vendors release Java EE 5 compliant application servers. So, in practice, this attribute should also be omitted.

Finally, the description attribute is a documentation parameter that can be accessed by the application server and displayed either through managed beans or through an administration user interface. It is therefore completely optional.

The minimal requirements then are (1) a standard Java interface and (2) a class that implements that interface and includes the @Stateless empty annotation, for example:

public interface MyBeanInterface {
 public void doSomething();
}

@Stateless
public class MyBean implements MyBeanInterface {
 public void doSomething() {
  ...
 }
}

And that is all that is required to build a session bean. Easy, huh?

Before looking at the source code for the Stateless Session Bean for our project, let's consider one important thing that Stateless Session Beans do: they interact with Entity Beans. Entity Beans are annotated POJOs, but the mechanism that is used to persist them to a database is a class called the Entity Manager. An Entity Manager implements the javax.persistence.EntityManager interface that provides methods to interact with a persistence context, which maintains unique object instances and provides methods to manage object persistence to a data source.

Section 3.1 of the EJB 3 persistence specification defines the EntityManager in detail, but because of its importance, I reproduced the interface in listing 14.

package javax.persistence;
/**
* Interface used to interact with the persistence context.
*/
public interface EntityManager {

 /**
 * Make an instance managed and persistent.
 * @param entity
 * @throws IllegalArgumentException if not an entity or entity is detached
 * @throws TransactionRequiredException if there is no transaction and the persistence context is
 * of type PersistenceContextType.TRANSACTION
 */
 public void persist(Object entity);

 /**
 * Merge the state of the given entity into the
 * current persistence context.
 * @param entity
 * @return the instance that the state was merged to
 * @throws IllegalArgumentException if instance is not an entity or is a removed entity
 * @throws TransactionRequiredException if there is no transaction and the persistence context is
 * of type PersistenceContextType.TRANSACTION
 */
 public <T> T merge(T entity);

 /**
 * Remove the entity instance.
 * @param entity
 * @throws IllegalArgumentException if not an entity
 * or if a detached entity
 * @throws TransactionRequiredException if there is
 * no transaction and the persistence context is
 * of type PersistenceContextType.TRANSACTION
 */
 public void remove(Object entity);

 /**
 * Find by primary key.
 * @param entityClass
 * @param primaryKey
 * @return the found entity instance or null
 * if the entity does not exist
 * @throws IllegalArgumentException if the first argument does
 * not denote an entity type or the second
 * argument is not a valid type for that
 * entity's primary key
 */
 public <T> T find(Class<T> entityClass, Object primaryKey);

 /**
 * Get an instance, whose state may be lazily fetched.
 * If the requested instance does not exist in the database,
 * throws EntityNotFoundException when the instance state is
 * first accessed. (The persistence provider runtime is
 * permitted to throw the EntityNotFoundException when
 * getReference is called.)
 * The application should not expect that the instance state will
 * be available upon detachment, unless it was accessed by the
 * application while the entity manager was open.
 * @param entityClass
 * @param primaryKey
 * @return the found entity instance
 * @throws IllegalArgumentException if the first argument does
 * not denote an entity type or the second
 * argument is not a valid type for that
 * entity's primary key
 * @throws EntityNotFoundException if the entity state
 * cannot be accessed
 */
 public <T> T getReference(Class<T> entityClass, Object primaryKey);

 /**
 * Synchronize the persistence context to the
 * underlying database.
 * @throws TransactionRequiredException if there is
 * no transaction
 * @throws PersistenceException if the flush fails
 */
 public void flush();

 /**
 * Set the flush mode that applies to all objects contained
 * in the persistence context.
 * @param flushMode
 */
 public void setFlushMode(FlushModeType flushMode);

 /**
 * Get the flush mode that applies to all objects contained
 * in the persistence context.
 * @return flushMode
 */
 public FlushModeType getFlushMode();

 /**
 * Set the lock mode for an entity object contained
 * in the persistence context.
 * @param entity
 * @param lockMode
 * @throws PersistenceException if an unsupported lock call
 * is made
 * @throws IllegalArgumentException if the instance is not
 * an entity or is a detached entity
 * @throws TransactionRequiredException if there is no
 * transaction
 */
 public void lock(Object entity, LockModeType lockMode);

 /**
 * Refresh the state of the instance from the database,
 * overwriting changes made to the entity, if any.
 * @param entity
 * @throws IllegalArgumentException if not an entity
 * or entity is not managed
 * @throws TransactionRequiredException if there is
 * no transaction and the persistence context is
 * of type PersistenceContextType.TRANSACTION
 * @throws EntityNotFoundException if the entity no longer
 * exists in the database
 */
 public void refresh(Object entity);

 /**
 * Clear the persistence context, causing all managed
 * entities to become detached. Changes made to entities that
 * have not been flushed to the database will not be
 * persisted.
 */
 public void clear();

 /**
 * Check if the instance belongs to the current persistence
 * context.
 * @param entity
 * @return
 * @throws IllegalArgumentException if not an entity
 */
 public boolean contains(Object entity);

 /**
 * Create an instance of Query for executing an
 * EJB QL statement.
 * @param ejbqlString an EJB QL query string
 * @return the new query instance
 * @throws IllegalArgumentException if query string is not valid
 */
 public Query createQuery(String ejbqlString);

 /**
 * Create an instance of Query for executing a
 * named query (in EJB QL or native SQL).
 * @param name the name of a query defined in metadata
 * @return the new query instance
 * @throws IllegalArgumentException if a query has not been
 * defined with the given name
 */
 public Query createNamedQuery(String name);

 /**
 * Create an instance of Query for executing
 * a native SQL statement, e.g., for update or delete.
 * @param sqlString a native SQL query string
 * @return the new query instance
 */
 public Query createNativeQuery(String sqlString);

 /**
 * Create an instance of Query for executing
 * a native SQL query.
 * @param sqlString a native SQL query string
 * @param resultClass the class of the resulting instance(s)
 * @return the new query instance
 */
 public Query createNativeQuery(String sqlString, Class resultClass);

 /**
 * Create an instance of Query for executing
 * a native SQL query.
 * @param sqlString a native SQL query string
 * @param resultSetMapping the name of the result set mapping
 * @return the new query instance
 */
 public Query createNativeQuery(String sqlString, String resultSetMapping);

 /**
 * Close an application-managed EntityManager.
 * After an EntityManager has been closed, all methods on the
 * EntityManager instance will throw the IllegalStateException
 * except for isOpen, which will return false.
 * This method can only be called when the EntityManager
 * is not associated with an active transaction.
 * @throws IllegalStateException if the EntityManager is
 * associated with an active transaction or if the
 * EntityManager is container-managed.
 */
 public void close();

 /**
 * Determine whether the EntityManager is open.
 * @return true until the EntityManager has been closed.
 */
 public boolean isOpen();

 /**
 * Return the resource-level transaction object.
 * The EntityTransaction instance may be used serially to
 * begin and commit multiple transactions.
 * @return EntityTransaction instance
 * @throws IllegalStateException if invoked on a JTA
 * EntityManager or an EntityManager that has been closed.
 */
 public EntityTransaction getTransaction();
}

The EntityManager is actually very easy to use, but as it is responsible for database persistence, the EJB container enforces the following transactional requirements:

  • The persist, merge, remove, flush, and refresh methods must be invoked within a transaction context; otherwise, it throws a javax.persistence.TransactionRequiredException.
  • The find and getReference methods are not required to be invoked within a transaction context.

As an example of how to use these methods, assume that an EntityManager named entityManager exists, and the code is executed within a transaction context. The following two lines insert a new row into the Forum table:

ForumBean forum = new ForumBean( "My Forum", "This is my forum" );
entityManager.persist( forum );

The following line queries the database for a message, given its primary key:

MessageBean message = entityManager.find( MessageBean.class, id );

Querying for collections of object is a little more difficult — not in terms of the EntityManager interface, but rather the EJB Query Language (EJB QL) that is used. It is similar to a subset of SQL and it defined explicitly in section 4 of the EJB3 Persistence specification.

The only thing to keep in mind when writing EJB QL queries is that it is the objects you are querying against, not the table names. So, for example, to select all forums, the query is executed against the ForumBean, not the forum table. And unless you need to access a particular bean field, the SELECT clause can be omitted. As a specific example, the SQL to select all forums from the forum table is:

SELECT * FROM forum;

And the EJB QL is:

FROM ForumBean

The SQL to select a forum with a specific name is:

SELECT * FROM forum WHERE name LIKE "Steve";

And the associated EJB QL is:

SELECT f FROM ForumBean WHERE f.name LIKE "Steve"

I highly recommend that you review section 4 of the EJB Persistence specification for more information, but for now I will explain each query on a case-by-case basis.

Acquiring an EntityManager from within a Stateless Session Bean could not be easier: define a variable of type EntityManager and include an @PersistenceContext annotation to it. The @PersistenceContext annotation is defined in listing 15.

Listing 15. @PersistenceContext Annotation

@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface PersistenceContext{
 String name() default "";
 String unitName() default "";
 PersistenceContextType type default TRANSACTION;
}

public enum PersistenceContextType {
 TRANSACTION,
 EXTENDED
}

The name attribute refers to the name by which the EntityManager and its persistence unit are to be known in the environment referencing context, but is not needed when dependency injection is used.

The type attribute defines whether the persistence context is transaction-scoped or extended. A transaction-scoped persistence context has a lifetime of a single transaction, while an extended persistence context has a lifetime that spans multiple transactions.

The unitName refers the name of the persistence unit. Persistence units are defined in the persistence.xml deployment descriptor; they define the JNDI name of the data sources that manage data persistence. The persistence.xml file is a deployment descriptor (one of the few in EJB3) through which you can define an entity manager. For example, the persistence.xml file that is used to connect the forum beans to the data source we created to connect to the forums database in MySQL is shown in listing 15.

Listing 16. persistence.xml

<?xml version="1.0" encoding="UTF-8"?>

<entity-manager>

 <name>forums</name>
 <jta-data-source>java:/ForumDS</jta-data-source>
 <properties>
  <property name="hibernate.hbm2ddl.auto"
       value="create-drop"/>
 </properties>
</entity-manager>

Through dependency injection, we can ask the EJB container to create an EntityManager in our session bean that accesses the persistence unit "forums." The EJB container will create the EntityManager for us and connect it to the java:/ForumDS data source. The following code excert, extracted from the ForumManager Stateless Session Bean, accomplishes this:

  @PersistenceContext (unitName="forums")
  protected EntityManager em;

With this infrastructure in place, listings 17 and 18 show the source code for the ForumManager interface and ForumManagerBean class, respectively. The ForumManagerBean is currently our catch-all session façade that manages all interactions with our entity beans.

Listing 17. ForumManager

package com.javasrc.forums.business;

// Import the standard Java classes
import java.util.*;

// Import our entity beans
import com.javasrc.forums.entity.*;

/**
 * The ForumManager interface defines all methods that will appear in the ForumManagerBean
 * stateless session bean
 */
public interface ForumManager
{
  /**
   * Adds the specified forum to the database
   * 
   * @param forumName     The name of the new forum
   * @param forumDescription A description of the new forum
   */
  public void addForum( String forumName, String forumDescription );

  /**
   * Given a forum name, find the forum and add the specified topic to the forum
   * 
   * @param topicName     The name of the new topic to add to the specified forum
   * @param forumName     The name of the forum to add the new topic to
   * @param topicDescription A description of the new topic
   */
  public void addTopicToForum( String topicName, String forumName, String topicDescription );
  
  /**
   * Returns all forums
   * 
   * @return All Forums in the database
   */
  public Collection <ForumBean> getForums();
  
  /**
   * Given a forum name, this method returns a collection containing all of the
   * forum’s topics.
   * 
   * @param forumName   The name of the forum from which to obtain topics
   * 
   * @return       A Collection of TopicBeans
   */
  public Collection <TopicBean> getTopics( String forumName );

  /**
   * Adds a new user to the system
   * 
   * @param name     The user’s name
   * @param login     The user’s login id
   * @param password   The user’s password
   */
  public void addUser( String name, String login, String password );

  /**
   * Posts the specified message to the appropriate forum’s topic.
   * 
   * @param forumName   The name of the forum to post this message to
   * @param topicName   The name of the topic to post this message to
   * @param login     The user login that is posting this message
   * @param title     The title of this message
   * @param body     The body of this message
   */
  public void postMessage( String forumName, String topicName, String login, String title, String body );

  /**
   * Returns all messages in the specified forum and topic
   * 
   * @param forumName   The name of the forum to locate the topic under
   * @param topicName   The name of the topic to retrieve messages from
   * 
   * @return       A Collection of MessageBeans
   */
  public Collection <MessageBean> getMessages( String forumName, String topicName );

  /**
   * Returns the message with the specified id
   * 
   * @param id      The primary key of the message to retieve
   * 
   * @return       The MessageBean for the message
   */
  public MessageBean getMessage( int id );
}

Listing 18. ForumManagerBean.java

package com.javasrc.forums.business;

// Import the EJB and persistence classes
import javax.ejb.*;
import javax.persistence.*;
import javax.annotation.Resource;

// Import the standard Java classes
import java.util.*;

// Import our entity beans
import com.javasrc.forums.entity.*;

/**
 * The ForumManagerBean is a stateless session bean that manages interactions with
 * Forums, Topics, and Messages
 */
@Stateless
public class ForumManagerBean implements ForumManager
{
  /**
   * Create an EntityManager to use with our Entity Beans
   */
  @PersistenceContext (unitName="forums")
  protected EntityManager em;

  /**
   * Adds the specified forum to the database
   * 
   * @param forumName     The name of the new forum
   * @param forumDescription A description of the new forum
   */
  public void addForum( String forumName, String forumDescription )
  {
    ForumBean forum = new ForumBean( forumName, forumDescription );
    this.em.persist( forum );
  }

  /**
   * Returns all forums
   * 
   * @return All Forums in the database
   */
  public Collection <ForumBean> getForums()
  {
    return this.em.createQuery( "FROM ForumBean" ).getResultList();
  }

  /**
   * Given a forum name, this method returns a collection containing all of the
   * forum’s topics.
   * 
   * @param forumName   The name of the forum from which to obtain topics
   * 
   * @return       A Collection of TopicBeans
   */
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public Collection <TopicBean> getTopics( String forumName )
  {
    // Create a query to locate our forum
    Query query = this.em.createQuery( "SELECT f FROM ForumBean f WHERE f.name LIKE :name" );
    query.setParameter( "name", forumName );

    // Retrieve the ForumBeans from the query and iterate over them
    Collection <ForumBean> rs = query.getResultList();
    for( ForumBean forum : rs )
    {
      // Return the first forum that matches our criteria
      return forum.getTopics();
    }
    return null;
  }

  /**
   * Given a forum name, find the forum and add the specified topic to the forum
   * 
   * @param topicName     The name of the new topic to add to the specified forum
   * @param forumName     The name of the forum to add the new topic to
   * @param topicDescription A description of the new topic
   */
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void addTopicToForum( String topicName, String forumName, String topicDescription )
  {
    try
    {
      // Create a query to find our forum
      Query query = this.em.createQuery( "SELECT f FROM ForumBean f WHERE f.name LIKE :name" );
      query.setParameter( "name", forumName );

      // Retrieve the results of our query and iterate over them
      Collection <ForumBean> rs = query.getResultList();
      for( ForumBean forum : rs )
      {
        // Create a new topic
        TopicBean newTopic = new TopicBean( topicName, topicDescription );

        // Add the new topic to the forum’s topics
        forum.getTopics().add( newTopic );

        // The the topic to whom he belongs
        newTopic.setForumBean( forum );

        // Persist the new topic to the database
        this.em.persist( newTopic );
      }
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }
  }
  
  /**
   * Adds a new user to the system
   * 
   * @param name     The user’s name
   * @param login     The user’s login id
   * @param password   The user’s password
   */
  public void addUser( String name, String login, String password )
  {
    UserBean user = new UserBean( name, login, password );
    this.em.persist( user );
  }

  /**
   * Posts the specified message to the appropriate forum’s topic.
   * 
   * @param forumName   The name of the forum to post this message to
   * @param topicName   The name of the topic to post this message to
   * @param login     The user login that is posting this message
   * @param title     The title of this message
   * @param body     The body of this message
   */
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void postMessage( String forumName, String topicName, String login, String title, String body )
  {
    try
    {
      // Create a new message
      MessageBean message = new MessageBean( title, body );

      // Create a query to find our forum, from which we will find our topic
      Query query = this.em.createQuery( "SELECT f FROM ForumBean f WHERE f.name LIKE :name" );
      query.setParameter( "name", forumName );

      // Retrieve the results of our query and iterate over them
      Collection <ForumBean> rs = query.getResultList();
      for( ForumBean forum : rs )
      {
        // Find our topics
        Collection <TopicBean> topics = forum.getTopics();
        for( TopicBean topic : topics )
        {
          if( topic.getName().equalsIgnoreCase( topicName ) )
          {
            // Found our topic
            message.setTopicBean( topic );
            topic.getMessages().add( message );
          }
        }
      }

      // Create a query to find our user
      Query userQuery = this.em.createQuery( "SELECT u FROM UserBean u WHERE u.login LIKE :login" );
      userQuery.setParameter( "login", login );

      // Iterate over our users (hopefully just one)
      Collection <UserBean> users = userQuery.getResultList();
      for( UserBean user : users )
      {
        message.setUserBean( user );
      }

      // Persist the new message to the database
      this.em.persist( message );
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }
  }

  /**
   * Returns all messages in the specified forum and topic
   * 
   * @param forumName   The name of the forum to locate the topic under
   * @param topicName   The name of the topic to retrieve messages from
   * 
   * @return       A Collection of MessageBeans
   */
  public Collection <MessageBean> getMessages( String forumName, String topicName )
  {
    try
    {
      // Create a query to find our forum, from which we will find our topic
      Query query = this.em.createQuery( "SELECT f FROM ForumBean f WHERE f.name LIKE :name" );
      query.setParameter( "name", forumName );

      // Retrieve the results of our query and iterate over them
      Collection <ForumBean> rs = query.getResultList();
      for( ForumBean forum : rs )
      {
        // Find our topics
        Collection <TopicBean> topics = forum.getTopics();
        for( TopicBean topic : topics )
        {
          if( topic.getName().equalsIgnoreCase( topicName ) )
          {
            // Found the topic, return its messages
            return topic.getMessages();
          }
        }
      }
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * Returns the message with the specified id
   * 
   * @param id      The primary key of the message to retieve
   * 
   * @return       The MessageBean for the message
   */
  public MessageBean getMessage( int id )
  {
    return this.em.find( MessageBean.class, id );
  }
}

The ForumManager defines methods to add and retrieve forums, topics, messages, and users. We may break apart this functionality into logical groups later, but for now we let this class serve as our main conduit to interacting with our data. The following summarizes the methods in the ForumManagerBean:

  • addForum(): this method adds a new forum to the database by creating a new ForumBean instances and calling the EntityManager's persist() method.
  • getForums(): this method returns all forums by creating a new EJB QL that requests all ForumBeans. The createQuery() method returns a javax.persistence.Query object (or rather an object that implements the Query interface.) If a Query has a single result, you can obtain it by calling its getSingleResult() object, otherwise you can retrieve all results by calling getResultList(). Because we were querying for ForumBeans, the result is a List of ForumBean instances.
  • getTopics(): this method is a little more complicated because it retrieves topics that belong to a specific forum. This means that the method must first locate the appropriate Forum by creating an initial query. This query is different from the others because it is parameterized by the inclusion of the :name parameter. Similar to the way you can parameterize prepared statements with question marks, you can parameterized EJB QL statements with named parameters and then call the resultant Query's setParameter() method to assign a value to that parameter. After we find the appropriate Forum, we call its getTopics() method to retrieve all of its topics.
  • addTopicToForum(): this method is implemented similar to the getTopics() method in the way that it first locates the appropriate topic, but once it locates the appropriate Forum it does three things: it adds the new TopicBean to the forum's list of topics (by calling getTopics().add( ... )); it calls the setForumBean() method on the new TopicBean to complete the relationship; and, finally, it calls the EntityManager's persist() method to commit the new TopicBean to the database. Considering that in a one-to-many relationship, the many object contains a foreign key reference back to the one object, so persisting the new TopicBean is all that is required.
  • addUser(): this method creates a new UserBean and calls the EntityManager's persist() method to commit the new user to the database.
  • postMessage(): this is the most complicated method in the ForumManagerBean because it has to locate the appropriate forum, then add the message to the appropriate topic within that forum, and finally locate the user that posted the message.
  • getMessages(): this method follows the same structure as postMessage(): locate the appropriate forum and then retrieve messages from the appropriate topic within that forum.
  • getMessage(): this method returns a single message through its primary key. It illustrates how to use the EntityManager find() method to locate a specific Entity Bean, given its primary key.

Summary

Thus far, in building our EJB 3 project, we saw a high-level overview of EJB3 and the simplifications that Java 5 annotations have provided to the EJB3 API. We set up a sample environment with JBoss and MySQL. We defined our forum application data model, built a set of Entity Beans, and used entity relationships to define relationships between them. Finally, we built a Stateless Session Bean that provided a programmatic front-end to interact and manage our Entity Beans.

Next week, we continue the example by building a simple Servlet that provides us with a mechanism to test out our code, so tune back in!

Online Resources

JBoss Download Page

MySQL 5.x Download Page

MySQL Connector/J Download Page

Official Documentation

JSR 220: Enterprise JavaBeans 3.0

Sun Documentation on Annotations

Discussions

Read and display the table in the document
Posted Nov 12, 2008 06:01 AM by StrongHead
1 Replies
Correction
Posted Nov 4, 2008 06:09 PM by youssef.mohammed
1 Replies
Instead of synchronising getInstance
Posted Nov 3, 2008 05:42 AM by grahamkelly
1 Replies

Make a New Comment

You must log in in order to post a comment.

Related Resources

Dustin SullivanIf You Are New to Java Programming...
By Dustin SullivanJune 2, 20092 Comments

We recently sat down with several top Java developers to talk about that state of the language as we approach this year's JavaOne.  As we were wrapping up, we threw one last question at them out of curiosity, and we thought you'd like to see what some of them said.

Steven HainesOracle Buys Sun of $7.4B
By Steven HainesApril 20, 2009 No Comments

In a stunning turn of events, Oracle steps in and buys Sun amist the breakdown of IBM's attempt to acquire Sun.

Steven HainesIBM in talks to buy Sun Microsystems for at least $6.5B
By Steven HainesMarch 18, 2009 No Comments

Reuters reported this morning that IBM is in talks to buy Sun Microsystems, which could "bolster their computer server products against rivals such as Hewlett-Packard Co."

See More Blogs

Informit Network