The i-Technology Media!
Register | Log in
   
 
.NET  ·  AJAX  ·  CLOUD  ·  ECLIPSE  ·  FLEX  ·  OPEN WEB  ·  iPHONE  ·  JAVA  ·  LINUX  ·  OPEN SOURCE  ·  ORACLE  ·  PBDJ  ·  SEARCH  ·  SILVERLIGHT  ·  SOA  ·  VIRTUALIZATION  ·  WEB 2.0  ·  WIRELESS  ·  XML
Comments
Drool, Britannia? Is the UK Failing the Cloud?
By Roger Strukhoff
Richard Davies wrote: The UK has a good crop of technology pioneers in cloud computing - for example ElasticHosts, FlexiScale, Flexiant, OnApp - and also some strong government initiatives such as G-Cloud. We will have to see whether this kind of technical leadership converts into swift mass-market adoption or not.
Jan. 8, 2012 11:38 AM EST
read more & respond »
Cloud Expo on Google News
Did you read today's front page stories & breaking news?

Cloud Expo & Virtualization 2011 West
Keynotes
Oracle
Opening Keynote | An Enterprise Cloud for Business-Critical Applications
Abiquo
Day 2 Keynote | The Enterprise Cloud Tightrope - Balancing for Success
Akamai
Day 3 Keynote | The DNA of an Enterprise Cloud
DIAMOND SPONSOR:
Oracle
Many Clouds, Many Choices'Cloud
PLATINUM PLUS SPONSORS:
Abiquo
Enterprise Cloud Best Practices - Town Hall - Join the discussion…
PLATINUM SPONSORS:
Intel
Progressing Toward the Federated, Automated and Client-Aware Cloud
New Relic
How to build an app with Twitter-like throughput
Rackspace
Computing in the Cloud Era
GOLD SPONSORS:
Gale Technologies
Practical Cloud Migration
IBM
Re-think IT. Re-inventing Business.
Intel/McAfee
Identity Driven Security in the Cloud
PerspecSys
Hackers Hackers Everywhere, Is My Public Cloud That Safe?
Red Hat
Unlock the Value of the Cloud
SHI
Mission Critical Applications and the Cloud - Myth or Reality?
SoftLayer
Not Your Grandpa's Cloud
Terremark
Integrating Enterprise Clouds
VMware
Upgrade to a vCloud
POWER PANELS:
Cloud Expo Silicon Valley: CTO Power Panel
Cloud Expo Silicon Valley: CEO Power Panel
Cloud Expo Silicon Valley: Cloud SuperStars Panel
Cloud Expo Silicon Valley: CloudNOW Panel
Click For 2010 West
Event Webcasts
Cloud Expo & Virtualization 2011 East
DIAMOND SPONSOR:
Dell
Dell & VMware Deliver the Enterprise Hybrid Cloud
PLATINUM PLUS SPONSORS:
Abiquo
Are Financial Services Organizations Risking Security by Avoiding Cloud Computing?
Oracle
From Consolidation to Enterprise Private PaaS
PLATINUM SPONSORS:
Intel
Driving the Transformation to Next Generation Cloud Data Centers
Rackspace
The Inevitability of an Open Cloud
GOLD SPONSORS:
CA Technologies
Follow YOUR path to Cloud Computing
Interxion
Who Keeps the Cloud in the Air?
Microsoft
Patterns for Cloud Computing
PerspecSys
War in the Clouds: Are you ready?
ServiceMesh
The Big Win: Stop Playing Small-Ball with Your Cloud Strategy
Terremark
Evaluating Enterprise Clouds
Xiotech
Cloud Storage: Myths and Realities
POWER PANELS:
Cloud Expo New York: CTO Power Panel
Cloud Expo New York: CEO Power Panel
Cloud Expo New York: CMO Power Panel
Cloud Expo New York: Wrap-Up Power Panel
Click For 2010 West
Event Webcasts
Live Google News by SYS-CON!
Top Three Links You Must Click On


Features
JPA Under the Hood
Understanding the Dynamics of Your JPA Framework

By: Alois Reitbauer
Nov. 24, 2009 01:17 PM

Java Developer Magazine on Ulitzer

I recently spoke on the behavior of different JPA frameworks at W-JAX(Germany) and TheServerSide Java Symposium (Prague). As some people have asked me, I am publishing the samples as well. I would also give away the Eclipse project, however, with all the third-party libraries I am sure I will end up not doing it correctly. In addition, I can add some comments on the samples and why they are as they are :-).

The goal of my experiment was to compare different JPA frameworks regarding their runtime characteristics. I addressed the following points:

  • Object Loading
  • Object Creation
  • Update Behaviour
  • Caching
  • Connection Handling

Preparation - SQL Scripts, Entity Classes and Persistence Unit Definitions
First start with the SQL scripts for creating the necessary tables. I use two tables - user and accounts. A user can have multiple accounts.


CREATE TABLE users (
`username`  VARCHAR(15)  NOT NULL,
`password`  VARCHAR(15)  NOT NULL,
`firstname` VARCHAR(30)  NOT NULL,
`lastname`  VARCHAR(30)  NOT NULL,
`street`    VARCHAR(30)  NOT NULL,
`town`      VARCHAR(15)  NOT NULL,
`zip`       VARCHAR(10)  NOT NULL,
PRIMARY KEY (id)
)

CREATE TABLE accounts (
`id`         INT(10)       NOT NULL AUTO_INCREMENT,
`IBAN`       VARCHAR(34)   NOT NULL,
`BIC`        VARCHAR(11)   NOT NULL,
`userID`     INT(10)  NOT NULL,
`amount`     DECIMAL(16,2) NOT NULL
PRIMARY KEY (id),
FOREIGN KEY (`userId`) REFERENCES `Users` (`id`),
)

Next we need to define the persistence classes. We define a User class and an Account class. Getter and setter methods are omitted for brevity here.

@Entity
@Table(name="users")
// @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class User {
private long id;
private String firstName;
private String lastName;
private String userName;
private String password;
private String street;
private String town;
private String zip; >
private List<Account> accounts;

@Id
public long getId() {
return id;
}

@OneToMany(mappedBy="user")
public List<Account> getAccounts (){
return accounts;
}

}

@Entity
@Table(name="accounts")
public class Account {

private long id;
private User user;
private String BIC;

@Id
public long getId() {
return id;
}

@ManyToOne
@JoinColumn(name="userID")
public User getUser(){
return user;
}
}

So far no rocket science. In the next step we define the persistence units. I defined a single unit per persistence provider. According to the JPA spec this should work fine. However some strange things might happen ;-)

<persistence  xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="netPayEclipse" transaction-type="RESOURCE_LOCAL"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" >

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> 

<!-- Entities -->
<class>com.dynatrace.talks.jpahood.entity.User</class>
<class>com.dynatrace.talks.jpahood.entity.Transaction</class>
<class>com.dynatrace.talks.jpahood.entity.Account</class>

<properties>
<property name="eclipselink.jdbc.user" value="root"/>
<property name="eclipselink.jdbc.password" value="admin" />
<property name="eclipselink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="eclipselink.jdbc.url" value="jdbc:mysql://localhost/netpay"/>
<property name="eclipselink.target-database" value="MySQL4" />
<!-- <property name="eclipselink.cache.shared.default" value="false"/> -->
<property name="eclipselink.jdbc.read-connections.min" value="1" />
<property name="eclipselink.jdbc.read-connections.max" value="1" />
<property name="eclipselink.jdbc.write-connections.min" value="1" />
<property name="eclipselink.jdbc.write-connections.max" value="1" />
</properties>
</persistence-unit>

<persistence-unit name="netPayOpenJPA" transaction-type="RESOURCE_LOCAL"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" >

<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>

<!-- Entities  -->
<class>com.dynatrace.talks.jpahood.entity.User</class>
<class>com.dynatrace.talks.jpahood.entity.Transaction</class>
<class>com.dynatrace.talks.jpahood.entity.Account</class>

<properties>
<property name="openjpa.ConnectionProperties"
value="DriverClassName=com.mysql.jdbc.Driver,
Url=jdbc:mysql://localhost/netpay,
MaxActive=1000,
MaxWait=10000,
TestOnBorrow=false,
Username=root,
Password=admin"/>
<property name="openjpa.ConnectionDriverName"
value="org.apache.commons.dbcp.BasicDataSource"/>
<!--
<property name="openjpa.DataCache" value="true"/>
<property name="openjpa.RemoteCommitProvider" value="sjvm"/>
-->
<property name="openjpa.QueryCache" 
value="CacheSize=1000, SoftReferenceSize=100"/>


</properties>
</persistence-unit>

<persistence-unit name="netPayHib" transaction-type="RESOURCE_LOCAL"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" >

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<!-- Entities  -->
<class>com.dynatrace.talks.jpahood.entity.User</class>
<class>com.dynatrace.talks.jpahood.entity.Transaction</class>
<class>com.dynatrace.talks.jpahood.entity.Account</class>

<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="admin"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost/netpay"/>
<property name="hibernate.max_fetch_depth" value="3"/>
<property name="hibernate.connection.pool_size" value="500"/>
<property name="hibernate.ejb.cfgfile"
value="/com/dynatrace/talks/jpahood/hibernate.cfg.xml"/>
</properties>
</persistence-unit>
</persistence>

That's it for preparation now we are ready to look at the samples, which will help us to understand the inner workings of JPA frameworks.

Dynamic Behaviour of JPA Frameworks
Now let us go through the various samples.  The samples are deliberately kept very simple. However they show typical usage scenarios

Sample 1- It depends on what you make out of it
The goal of this sample is to test whether a framework detects parameters in query strings and automatically creates proper prepared statements. Here is the sample for querying the user with id 1.

public static void simpleLoadSample() {

EntityManager em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select u from User u where u.id=1");

iterateOverItems(query.getResultList());

em.close();

}

Actually a JPA framework should produce the same SQL statement as for the code below.

public static void simpleLoadwithParameter() {

EntityManager em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select u from User u where u.id=?");

query.setParameter(1, 1L);

iterateOverItems(query.getResultList());

em.close();

}

In my tests both - OpenJPA and EclipseLink - create proper prepared statements. However Hibernate creates a statement that looks like this "select ... from user where id=1″ and also prepares this statement. Prepared statements like this can have render PreparedStatement caching as well as database query caching obsolete.

Sample 2 - The Magic Value
This sample deals with object construction. What I have seen in my presentation a lot of people are not sure what is actually happening here.  We are loading an object with are query. While waiting for input, we modify the value in the database and then we query the value again.

public static void loadTwiceWithQuery (){

EntityManager em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select u from User u where u.id=1");

iterateOverItems(query.getResultList());

em.close();

try {

System.in.read();
// change value in database

} catch (IOException e) {

e.printStackTrace();

}

em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

query = em.createQuery("select u from User u where u.id=1");

iterateOverItems(query.getResultList());

em.close();

}

When trying this example with different JPA frameworks you will see that two database queries will be executed unless query caching is enabled. However the second query will return the object with the "old" values. Why that?  The query is used to retrieve the id of the user. As it realized that the object has already been loaded it will not construct that object again.  In case you always want the latest state, you would have to use refresh().

Sample 3 - Staying up to date
In this sample we look at updating. We load again a user update and then update the first name in a very creative way ;-).

public static void simpleUpdate (){

EntityManager em1= EntityManagerUtil.getEMFactory(provider).createEntityManager();

em1.getTransaction().begin();

User user = em1.find(User.class, 1L);

user.setFirstName("otherFirstName" + System.currentTimeMillis());

em1.getTransaction().commit();

em1.close ();

}

Guess what happens ... the object get's updated ;-). Well, that is what you would expect. The interesting part here is again, what the statement looks like. Actually we only want the firstame column to be updated. EclipseLink and OpenJPA do so by default. Hibernate however will update all fields. In case you have defined trigger in the database this can cause serious performance problems as triggers or stored procedures might be invoked although they should not.

Sample 4 - Having good references
This sample deal with the getReference method of the EntityManager. The JavaDoc says:

Get an instance, whose state may be lazily fetched. ... 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.

Hmmm, I do not know how you feel about this, but the word may confused me here a bit. Actually this means I do not know whether the object will be fetched or not. I used the following code sample to see what's happening

public static void getReferenceSample (){

EntityManager em= EntityManagerUtil.getEMFactory(provider).createEntityManager();

em.getReference(User.class, 1L);

em.close ();

}

Here my experiments show that eclipseLink loads the data while Hibernate and OpenJPA do not load the data.

Sample 5 - Staying in good relations
In the next sample we look at the behaviour for loading detail-master relationships. Hey, that is master detail not the other way round. Yes, I know but here we first load the detail and then the master.

public static void loadRelationSample () {

EntityManager em= EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select acc from Account acc where acc.id = 1");

Account account = (Account) query.getSingleResult();

User user = account.getUser();

em.close ();

}

Very interestingly all frameworks I used by default load the master record as well. How they actually do this depends on the framework as well as the database used. OpenJPA for example by default uses a join, eclipseLink does not and when using Hibernate it depends on the used dialect (and database).

Sample 6 - Yam Session
In this sample we look at connection handling and sessions. The first example creates more and more EntityManager and queries for an object. The second sample does the same, however it also uses transactions. ... and what is the ArrayList for? Well, we want to avoid Garbage Collection and automatic closing of the EntityManager

public static void checkMaxSessions() {

ArrayList<EntityManager> myEMs = new ArrayList<EntityManager>();

for (int i = 1; i < 51; ++i) {

try {

EntityManager em = EntityManagerUtil.getEMFactory(provider)

.createEntityManager();

myEMs.add(em);

User u = (User) em.find(User.class, new Long(i));

u.getFirstName();

System.out.println("Concurrent sessions: " + i);

} catch (Exception ex) {

System.err.println(ex);

break;

}

try {

Thread.sleep(700);

} catch (InterruptedException e) {

}

}

}

public static void checkMaxSessionsWithTransaction() {

ArrayList<EntityManager> myEMs = new ArrayList<EntityManager>();

for (int i = 1; i < 51; ++i) {

try {

EntityManager em = EntityManagerUtil.getEMFactory(provider)

.createEntityManager();

myEMs.add(em);

em.getTransaction().begin();

User u = (User) em.find(User.class, new Long(i));

u.getFirstName();

System.out.println("Concurrent sessions: " + (i));

} catch (Exception ex) {

System.err.println(ex);

break;

}

try {

Thread.sleep(300);

} catch (InterruptedException e) {

}

}

}

EntityManager em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select u from User u where u.id=1″);

iterateOverItems(query.getResultList());

em.close();

}

public static void simpleLoadwithParameter() {

EntityManager em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select u from User u where u.id=?");

query.setParameter(1, 1L);

iterateOverItems(query.getResultList());

em.close();

}

public static void loadTwiceWithQuery (){

EntityManager em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select u from User u where u.id=1″);

iterateOverItems(query.getResultList());

em.close();

try {

System.in.read();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

em = EntityManagerUtil.getEMFactory(provider).createEntityManager();

query = em.createQuery("select u from User u where u.id=1″); /* 2 */

iterateOverItems(query.getResultList());

em.close();


}

public static void simpleUpdate (){

EntityManager em1= EntityManagerUtil.getEMFactory(provider).createEntityManager();

em1.getTransaction().begin();

User user = em1.find(User.class, 1L);

user.setFirstName("otherFirstName" + System.currentTimeMillis());

// em1.persist(user);

em1.getTransaction().commit();

em1.close ();

}


public static void updateToSameValue (){


EntityManager em1= EntityManagerUtil.getEMFactory(provider).createEntityManager();

em1.getTransaction().begin();

User user = em1.find(User.class, 1L);

user.setFirstName(user.getFirstName());

em1.persist(user);

em1.getTransaction().commit();

em1.close ();

}


public static void getReferenceSample (){

EntityManager em= EntityManagerUtil.getEMFactory(provider).createEntityManager();

em.getReference(User.class, 1L);

}

 

public static void loadRelationSample () {

EntityManager em= EntityManagerUtil.getEMFactory(provider).createEntityManager();

Query query = em.createQuery("select acc from Account acc where acc.id = 1″);

Account account = (Account) query.getSingleResult();

User user = account.getUser();

em.close ();

}

public static void checkMaxSessions() {

ArrayList<EntityManager> myEMs = new ArrayList<EntityManager>();

for (int i = 1; i < 51; ++i) {

try {

EntityManager em = EntityManagerUtil.getEMFactory(provider)

.createEntityManager();

myEMs.add(em);

User u = (User) em.find(User.class, new Long(i));

u.getFirstName();

System.out.println("Concurrent sessions: " + i);

} catch (Exception ex) {

System.err.println(ex);

break;

}

try {

Thread.sleep(700);

} catch (InterruptedException e) {

}

}

}


public static void checkMaxSessionsWithTransaction() {

ArrayList<EntityManager> myEMs = new ArrayList<EntityManager>();

for (int i = 1; i < 51; ++i) {

try {

EntityManager em = EntityManagerUtil.getEMFactory(provider)

.createEntityManager();

myEMs.add(em);

em.getTransaction().begin();

User u = (User) em.find(User.class, new Long(i));

u.getFirstName();

System.out.println("Concurrent sessions: " + (i));

} catch (Exception ex) {

System.err.println(ex);

break;

}

try {

Thread.sleep(300);

} catch (InterruptedException e) {

}

}

}

What we can see here that when using no transactions, we can do all the work with one connection.  When we use transactions however Hibernate will open a new connection per EntityManager. So, if you do not need transactions - when you just load a list on a website for example - you are better off not using them.

Conclusion
Although JPA is standardizing the interface for persistence frameworks there is still a lot of freedom regarding runtime behaviour. This can easily impact the performance of your application. It also shows that you should not rely on the default settings of framework. In case you need consistent behaviour across JPA providers, you have to thest the runtime behaviour and tweek it to your needs. Ideally you write something like Reset CSS for JPA.

Further Reading
Below you find a number of links to other persistence-related posts. Specifically to caching in Hibernate. Additionally I recommend checking out the database diagnosis section of dynaTrace.

Related reading:

  1. Understanding Caching in Hibernate - Part Three : The Second Level Cache In the last posts I already covered the session cache...
  2. Understanding Caching in Hibernate - Part Two : The Query Cache In the last post I wrote on caching in Hibernate...
  3. Understanding Caching in Hibernate - Part One : The Session Cache Hibernate offers caching functionality which is designed to reduces...
  4. JPA Frameworks under the Hood @ TheServerSide Prague [caption id="attachment_981" align="alignleft" width="125" caption="TSS Prague I'm speaking"][/caption] For all...
  5. ADO.NET Entity Framework: unexpected behaviour with MergeOptions I've started to look closer at the ADO.NET Entity Framework...
Published Nov. 24, 2009— Reads 5,831
Copyright © 2009 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
Related Stories
▪ Azure Dynamics Announces Memorandum of Understanding With Kidron for Under-the-Hood Power Systems
▪ Working with Table Storage on the Windows Azure November 2009 CTP
▪ Linux.SYS-CON.com Feature: Novell OES, The Leading Linux Server Product vs. Windows 2003, Server
About Alois Reitbauer
Alois Reitbauer works as a Technology Strategist for dynaTrace Software where he is leading the Methods and Technology team. As part of the R&D team he influences the dynaTrace product strategy and works closely with key customers in implementing performance management solution for the entire lifecylce. Alois has 10 years experience as architect and developer in the Java and .NET space. He is a frequent speaker at technology conferences on performance and architecture related topics and regularly publishes articles blogs on blog.dynatrace.com

Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
Click to Add our RSS Feeds to the Service of Your Choice:
Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
Publish Your Article! Please send it to editorial(at)sys-con.com!

Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021

SYS-CON Featured Whitepapers

ADS BY GOOGLE

Breaking Java News
AXON Video System Captures Officers Using TASER X26 Saving Suicidal Man
Faruqi & Faruqi, LLP is Investigating Parametric Technology Corporation on Behalf of its Shareholders (PMTC)
GOOD MORNING
POWERVAR Will Release the Mobile Power Manager (MPM) at the Upcoming HIMSS '12 Annual Conference

ADVERTISE   |   MAGAZINE SUBSCRIPTIONS   |   FREE BREAKING-NEWSLETTERS!   |   SYS-CON.TV   |   BLOG-N-PLAY!   |   WEBCAST   |   EDUCATION   |   RESEARCH

.NET Developer's Journal - .NETDJ   |   ColdFusion Developer's Journal - CFDJ   |   Eclipse Developer's Journal - EDJ   |   Enterprise Open Source Magazine - EOS
Open Web Developer's Journal - OPENWEB   |   iPhone Developer's Journal - iPHONE   |   Virtualization - Virtualization   |   Java Developer's Journal - JDJ   |   Linux.SYS-CON.com
PowerBuilder Developer's Journal - PBDJ   |   SEO / SEM Journal - SJ   |   SOAWorld Magazine - SOAWM   |   IT Solutions Guide - ITSG   |   Symbian Developer's Journal - SDJ
WebLogic Developer's Journal - WLDJ   |   WebSphere Journal - WJ   |   Wireless Business & Technology - WBT   |   XML-Journal - XMLJ   |   Internet Video - iTV
Flex Developer's Journal - Flex   |   AJAXWorld Magazine - AWM   |   Silverlight Developer's Journal - SLDJ   |   PHP.SYS-CON.com   |   Web 2.0 Journal - WEB2
Apache   |   CMS   |   CRM   |   HP   |   Oracle Journal   |   Perl   |   Python   |   Red Hat   |   Ruby on Rails   |   SAP   |   SaaS

SYS-CON MEDIA:   ABOUT US   |   CONTACT US   |   COMPANY NEWS   |   CAREERS   |   SITE MAP
SYS-CON EVENTS:   |  AJAXWorld Conference & Expo  |  iPhone Developer Summit  |  Cloud Computing Conference & Expo  |  SOA World Conference & Expo  |  Virtualization Conference & Expo
INTERNATIONAL SITES:   India  |  U.K.  |  Canada  |  Germany  |  France  |  Australia  |  Italy  |  Spain  |  Netherlands  |  Brazil  |  Belgium
 Terms of Use & Our Privacy Statement     About Newsfeeds / Video Feeds
Copyright ©1994-2008 SYS-CON Publications, Inc. All Rights Reserved. All marks are trademarks of SYS-CON Media.
Reproduction in whole or in part in any form or medium without express written permission of SYS-CON Publications, Inc. is prohibited.
 
close this window